Repository: RegrowthStudios/SoACode-Public Branch: develop Commit: c3ddd69355b5 Files: 497 Total size: 2.7 MB Directory structure: gitextract__ur0a12_/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .mailmap ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.txt ├── ModTest/ │ ├── ModTest.vcxproj │ ├── ModTest.vcxproj.filters │ └── main.cpp ├── ProjectConverter/ │ ├── App.Designer.cs │ ├── App.config │ ├── App.cs │ ├── App.resx │ ├── ProjectConverter.csproj │ ├── ProjectConverter.sln │ └── Properties/ │ └── AssemblyInfo.cs ├── README.md ├── SoA/ │ ├── AABBCollidableComponentUpdater.cpp │ ├── AABBCollidableComponentUpdater.h │ ├── ARProcessor.cpp │ ├── ARProcessor.h │ ├── AmbienceLibrary.cpp │ ├── AmbienceLibrary.h │ ├── AmbiencePlayer.cpp │ ├── AmbiencePlayer.h │ ├── AmbienceStream.cpp │ ├── AmbienceStream.h │ ├── Animation.h │ ├── App.cpp │ ├── App.h │ ├── AtmosphereComponentRenderer.cpp │ ├── AtmosphereComponentRenderer.h │ ├── AxisRotationComponentUpdater.cpp │ ├── AxisRotationComponentUpdater.h │ ├── Biome.cpp │ ├── Biome.h │ ├── BlendState.h │ ├── BlockData.cpp │ ├── BlockData.h │ ├── BlockLoader.cpp │ ├── BlockLoader.h │ ├── BlockPack.cpp │ ├── BlockPack.h │ ├── BlockTexture.cpp │ ├── BlockTexture.h │ ├── BlockTextureAtlas.h │ ├── BlockTextureLoader.cpp │ ├── BlockTextureLoader.h │ ├── BlockTextureMethods.cpp │ ├── BlockTextureMethods.h │ ├── BlockTexturePack.cpp │ ├── BlockTexturePack.h │ ├── BloomRenderStage.cpp │ ├── BloomRenderStage.h │ ├── CAEngine.cpp │ ├── CAEngine.h │ ├── CMakeLists.txt │ ├── Camera.cpp │ ├── Camera.h │ ├── CellularAutomataTask.cpp │ ├── CellularAutomataTask.h │ ├── Chunk.cpp │ ├── Chunk.h │ ├── ChunkAccessor.cpp │ ├── ChunkAccessor.h │ ├── ChunkAllocator.cpp │ ├── ChunkAllocator.h │ ├── ChunkGenerator.cpp │ ├── ChunkGenerator.h │ ├── ChunkGrid.cpp │ ├── ChunkGrid.h │ ├── ChunkGridRenderStage.cpp │ ├── ChunkGridRenderStage.h │ ├── ChunkHandle.h │ ├── ChunkID.h │ ├── ChunkIOManager.cpp │ ├── ChunkIOManager.h │ ├── ChunkMesh.cpp │ ├── ChunkMesh.h │ ├── ChunkMeshManager.cpp │ ├── ChunkMeshManager.h │ ├── ChunkMeshTask.cpp │ ├── ChunkMeshTask.h │ ├── ChunkMesher.cpp │ ├── ChunkMesher.h │ ├── ChunkQuery.cpp │ ├── ChunkQuery.h │ ├── ChunkRenderer.cpp │ ├── ChunkRenderer.h │ ├── ChunkSphereComponentUpdater.cpp │ ├── ChunkSphereComponentUpdater.h │ ├── ChunkUpdater.cpp │ ├── ChunkUpdater.h │ ├── ClientState.h │ ├── CloseTerrainPatch.cpp │ ├── CloudsComponentRenderer.cpp │ ├── CloudsComponentRenderer.h │ ├── Collision.cpp │ ├── Collision.h │ ├── CollisionComponentUpdater.cpp │ ├── CollisionComponentUpdater.h │ ├── ColorFilterRenderStage.cpp │ ├── ColorFilterRenderStage.h │ ├── ColoredFullQuadRenderer.cpp │ ├── ColoredFullQuadRenderer.h │ ├── CommonState.h │ ├── Computer.cpp │ ├── Computer.h │ ├── ConsoleFuncs.h │ ├── ConsoleMain.h │ ├── ConsoleTests.cpp │ ├── ConsoleTests.h │ ├── Constants.h │ ├── CutoutVoxelRenderStage.cpp │ ├── CutoutVoxelRenderStage.h │ ├── DLLAPI.h │ ├── DLLLoader.h │ ├── DebugRenderer.cpp │ ├── DebugRenderer.h │ ├── Density.cpp │ ├── Density.h │ ├── DevConsole.cpp │ ├── DevConsole.h │ ├── DevConsoleView.cpp │ ├── DevConsoleView.h │ ├── DevHudRenderStage.cpp │ ├── DevHudRenderStage.h │ ├── DevScreen.cpp │ ├── DevScreen.h │ ├── Doxyfile.in │ ├── DualContouringMesher.cpp │ ├── DualContouringMesher.h │ ├── ECS Specs.txt │ ├── ECSTemplates.cpp │ ├── ECSTemplates.h │ ├── Errors.cpp │ ├── Errors.h │ ├── ExposureCalcRenderStage.cpp │ ├── ExposureCalcRenderStage.h │ ├── FarTerrainComponentRenderer.cpp │ ├── FarTerrainComponentRenderer.h │ ├── FarTerrainComponentUpdater.cpp │ ├── FarTerrainComponentUpdater.h │ ├── FarTerrainPatch.cpp │ ├── FarTerrainPatch.h │ ├── Flora.cpp │ ├── Flora.h │ ├── FloraGenerator.cpp │ ├── FloraGenerator.h │ ├── FragFile.cpp │ ├── FragFile.h │ ├── FreeMoveComponentUpdater.cpp │ ├── FreeMoveComponentUpdater.h │ ├── Frustum.cpp │ ├── Frustum.h │ ├── FrustumComponentUpdater.cpp │ ├── FrustumComponentUpdater.h │ ├── GameManager.cpp │ ├── GameManager.h │ ├── GamePlayScreen.cpp │ ├── GamePlayScreen.h │ ├── GameRenderParams.cpp │ ├── GameRenderParams.h │ ├── GameSystem.cpp │ ├── GameSystem.h │ ├── GameSystemAssemblages.cpp │ ├── GameSystemAssemblages.h │ ├── GameSystemComponentBuilders.cpp │ ├── GameSystemComponentBuilders.h │ ├── GameSystemComponents.cpp │ ├── GameSystemComponents.h │ ├── GameSystemUpdater.cpp │ ├── GameSystemUpdater.h │ ├── GameplayLoadScreen.cpp │ ├── GameplayLoadScreen.h │ ├── GameplayRenderer.cpp │ ├── GameplayRenderer.h │ ├── GasGiantComponentRenderer.cpp │ ├── GasGiantComponentRenderer.h │ ├── GenerateTask.cpp │ ├── GenerateTask.h │ ├── GeometrySorter.cpp │ ├── GeometrySorter.h │ ├── HdrRenderStage.cpp │ ├── HdrRenderStage.h │ ├── HeadComponentUpdater.cpp │ ├── HeadComponentUpdater.h │ ├── IRenderStage.h │ ├── ImageAssetLoader.cpp │ ├── ImageAssetLoader.h │ ├── IniParser.cpp │ ├── IniParser.h │ ├── InitScreen.cpp │ ├── InitScreen.h │ ├── InputMapper.cpp │ ├── InputMapper.h │ ├── Inputs.cpp │ ├── Inputs.h │ ├── Item.cpp │ ├── Item.h │ ├── LenseFlareRenderer.cpp │ ├── LenseFlareRenderer.h │ ├── LiquidData.h │ ├── LiquidVoxelRenderStage.cpp │ ├── LiquidVoxelRenderStage.h │ ├── LoadBar.cpp │ ├── LoadBar.h │ ├── LoadContext.h │ ├── LoadMonitor.cpp │ ├── LoadMonitor.h │ ├── LoadTaskBlockData.h │ ├── LoadTaskGameManager.h │ ├── LoadTaskStarSystem.h │ ├── LoadTaskTextures.h │ ├── MTRenderState.h │ ├── MTRenderStateManager.cpp │ ├── MTRenderStateManager.h │ ├── MainMenuLoadScreen.cpp │ ├── MainMenuLoadScreen.h │ ├── MainMenuRenderer.cpp │ ├── MainMenuRenderer.h │ ├── MainMenuScreen.cpp │ ├── MainMenuScreen.h │ ├── MainMenuScriptedUI.cpp │ ├── MainMenuScriptedUI.h │ ├── MainMenuSystemViewer.cpp │ ├── MainMenuSystemViewer.h │ ├── MarchingCubesTable.h │ ├── MaterialAtlas.h │ ├── MaterialData.h │ ├── MaterialStack.h │ ├── MetaSection.cpp │ ├── MetaSection.h │ ├── ModInformation.h │ ├── ModPathResolver.cpp │ ├── ModPathResolver.h │ ├── ModelMesher.cpp │ ├── ModelMesher.h │ ├── MusicPlayer.cpp │ ├── MusicPlayer.h │ ├── NightVisionRenderStage.cpp │ ├── NightVisionRenderStage.h │ ├── Noise.cpp │ ├── Noise.h │ ├── Octree.cpp │ ├── Octree.h │ ├── OpaqueVoxelRenderStage.cpp │ ├── OpaqueVoxelRenderStage.h │ ├── OptionsController.cpp │ ├── OptionsController.h │ ├── OrbitComponentRenderer.cpp │ ├── OrbitComponentRenderer.h │ ├── OrbitComponentUpdater.cpp │ ├── OrbitComponentUpdater.h │ ├── PDA.cpp │ ├── PDA.h │ ├── ParkourComponentUpdater.cpp │ ├── ParkourComponentUpdater.h │ ├── ParticleMesh.h │ ├── PauseMenu.cpp │ ├── PauseMenu.h │ ├── PauseMenuRenderStage.cpp │ ├── PauseMenuRenderStage.h │ ├── PdaRenderStage.cpp │ ├── PdaRenderStage.h │ ├── PhysicsBlockRenderStage.cpp │ ├── PhysicsBlockRenderStage.h │ ├── PhysicsComponentUpdater.cpp │ ├── PhysicsComponentUpdater.h │ ├── Planet.cpp │ ├── Planet.h │ ├── PlanetGenData.cpp │ ├── PlanetGenData.h │ ├── PlanetGenLoader.cpp │ ├── PlanetGenLoader.h │ ├── PlanetGenerator.cpp │ ├── PlanetGenerator.h │ ├── PlanetHeightData.h │ ├── PlanetRenderStage.cpp │ ├── PlanetRenderStage.h │ ├── PlanetRingsComponentRenderer.cpp │ ├── PlanetRingsComponentRenderer.h │ ├── Positional.h │ ├── ProceduralChunkGenerator.cpp │ ├── ProceduralChunkGenerator.h │ ├── ProgramGenDelegate.h │ ├── RegionFileManager.cpp │ ├── RegionFileManager.h │ ├── RenderUtils.h │ ├── Resources/ │ │ └── resources.rc │ ├── SOA.vcxproj │ ├── SOA.vcxproj.filters │ ├── SOA.vcxproj.yml │ ├── ShaderAssetLoader.cpp │ ├── ShaderAssetLoader.h │ ├── ShaderLoader.cpp │ ├── ShaderLoader.h │ ├── SkyboxRenderStage.cpp │ ├── SkyboxRenderStage.h │ ├── SkyboxRenderer.cpp │ ├── SkyboxRenderer.h │ ├── SmartVoxelContainer.hpp │ ├── SoAState.h │ ├── SoaController.cpp │ ├── SoaController.h │ ├── SoaEngine.cpp │ ├── SoaEngine.h │ ├── SoaFileSystem.cpp │ ├── SoaFileSystem.h │ ├── SoaOptions.cpp │ ├── SoaOptions.h │ ├── SoaState.cpp │ ├── SonarRenderStage.cpp │ ├── SonarRenderStage.h │ ├── SpaceSystem.cpp │ ├── SpaceSystem.h │ ├── SpaceSystemAssemblages.cpp │ ├── SpaceSystemAssemblages.h │ ├── SpaceSystemComponentBuilders.cpp │ ├── SpaceSystemComponentBuilders.h │ ├── SpaceSystemComponentTables.cpp │ ├── SpaceSystemComponentTables.h │ ├── SpaceSystemComponents.h │ ├── SpaceSystemLoadStructs.cpp │ ├── SpaceSystemLoadStructs.h │ ├── SpaceSystemLoader.cpp │ ├── SpaceSystemLoader.h │ ├── SpaceSystemRenderStage.cpp │ ├── SpaceSystemRenderStage.h │ ├── SpaceSystemUpdater.cpp │ ├── SpaceSystemUpdater.h │ ├── SphericalHeightmapGenerator.cpp │ ├── SphericalHeightmapGenerator.h │ ├── SphericalTerrainComponentRenderer.cpp │ ├── SphericalTerrainComponentRenderer.h │ ├── SphericalTerrainComponentUpdater.cpp │ ├── SphericalTerrainComponentUpdater.h │ ├── SphericalVoxelComponentUpdater.cpp │ ├── SphericalVoxelComponentUpdater.h │ ├── SsaoRenderStage.cpp │ ├── SsaoRenderStage.h │ ├── StarComponentRenderer.cpp │ ├── StarComponentRenderer.h │ ├── Startup.cpp │ ├── Startup.h │ ├── SystemARRenderer.cpp │ ├── SystemARRenderer.h │ ├── SystemBodyLoader.cpp │ ├── SystemBodyLoader.h │ ├── TerrainGenTextures.cpp │ ├── TerrainGenTextures.h │ ├── TerrainPatch.cpp │ ├── TerrainPatch.h │ ├── TerrainPatchConstants.h │ ├── TerrainPatchMesh.cpp │ ├── TerrainPatchMesh.h │ ├── TerrainPatchMeshManager.cpp │ ├── TerrainPatchMeshManager.h │ ├── TerrainPatchMeshTask.cpp │ ├── TerrainPatchMeshTask.h │ ├── TerrainPatchMesher.cpp │ ├── TerrainPatchMesher.h │ ├── TestBiomeScreen.cpp │ ├── TestBiomeScreen.h │ ├── TestBlockViewScreen.cpp │ ├── TestBlockViewScreen.h │ ├── TestConnectedTextureScreen.cpp │ ├── TestConnectedTextureScreen.h │ ├── TestConsoleScreen.cpp │ ├── TestConsoleScreen.h │ ├── TestDeferredScreen.cpp │ ├── TestDeferredScreen.h │ ├── TestDisplacementMappingScreen.cpp │ ├── TestDisplacementMappingScreen.h │ ├── TestGasGiantScreen.cpp │ ├── TestGasGiantScreen.h │ ├── TestMappingScreen.cpp │ ├── TestMappingScreen.h │ ├── TestNewBlockAPIScreen.cpp │ ├── TestNewBlockAPIScreen.h │ ├── TestNoiseScreen.cpp │ ├── TestNoiseScreen.h │ ├── TestPlanetGenScreen.cpp │ ├── TestPlanetGenScreen.h │ ├── TestScriptScreen.cpp │ ├── TestScriptScreen.h │ ├── TestStarScreen.cpp │ ├── TestStarScreen.h │ ├── TestUIScreen.cpp │ ├── TestUIScreen.h │ ├── TestVoxelModelScreen.cpp │ ├── TestVoxelModelScreen.h │ ├── Thread.h │ ├── TransparentVoxelRenderStage.cpp │ ├── TransparentVoxelRenderStage.h │ ├── VRayHelper.cpp │ ├── VRayHelper.h │ ├── Vertex.h │ ├── VirtualKeyKegDef.inl │ ├── VoxPool.cpp │ ├── VoxPool.h │ ├── VoxelBits.h │ ├── VoxelCoordinateSpaces.h │ ├── VoxelEditor.cpp │ ├── VoxelEditor.h │ ├── VoxelLightEngine.cpp │ ├── VoxelLightEngine.h │ ├── VoxelMatrix.cpp │ ├── VoxelMatrix.h │ ├── VoxelMesh.h │ ├── VoxelMesher.cpp │ ├── VoxelMesher.h │ ├── VoxelModel.cpp │ ├── VoxelModel.h │ ├── VoxelModelLoader.cpp │ ├── VoxelModelLoader.h │ ├── VoxelModelMesh.cpp │ ├── VoxelModelMesh.h │ ├── VoxelModelRenderer.cpp │ ├── VoxelModelRenderer.h │ ├── VoxelNavigation.inl │ ├── VoxelNodeSetter.cpp │ ├── VoxelNodeSetter.h │ ├── VoxelNodeSetterTask.cpp │ ├── VoxelNodeSetterTask.h │ ├── VoxelRay.cpp │ ├── VoxelRay.h │ ├── VoxelSpaceConversions.cpp │ ├── VoxelSpaceConversions.h │ ├── VoxelSpaceUtils.cpp │ ├── VoxelSpaceUtils.h │ ├── VoxelUpdateBufferer.h │ ├── VoxelUpdateOrder.inl │ ├── VoxelUtils.h │ ├── VoxelVertices.h │ ├── WSO.cpp │ ├── WSO.h │ ├── WSOAtlas.cpp │ ├── WSOAtlas.h │ ├── WSOData.h │ ├── WSOScanner.cpp │ ├── WSOScanner.h │ ├── WorldIO.cpp │ ├── WorldIO.h │ ├── WorldStructs.cpp │ ├── WorldStructs.h │ ├── ZipFile.cpp │ ├── ZipFile.h │ ├── app.config │ ├── atomicops.h │ ├── errorlog.txt │ ├── main.cpp │ ├── qef.cpp │ ├── qef.h │ ├── readerwriterqueue.h │ ├── soaUtils.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── svd.cpp │ ├── svd.h │ └── textureUtils.h ├── VorbCopy.bat ├── appveyor.yml ├── build.bat ├── build.sh ├── cmake/ │ └── hunter/ │ ├── HunterGate.cmake │ ├── cache.cmake │ └── passwords.cmake ├── jenkins.py ├── scripts/ │ ├── ShowPredefined.cpp │ ├── append-boost-config-macros.cmake.in │ ├── autotools-merge-lipo.cmake.in │ ├── clear-all.cmake │ ├── clear-all.sh │ ├── copy-files.cmake │ ├── create-predefined-list.py │ ├── create-toolchain-info.cmake │ ├── find_python.cmake │ ├── link-all.cmake │ ├── link-all.py │ ├── pkgconfig-export-targets.cmake.in │ ├── predefined.list │ ├── try-copy-license.cmake │ └── upload-cache-to-github.py └── utils/ └── git-hooks/ ├── commit-msg ├── commit-msg_check-spelling ├── cpplint/ │ ├── __init__.py │ └── cpplint.py ├── pep8.py ├── pre-commit ├── pre-commit_check-for-cpp-style ├── pre-commit_check-for-pdb ├── pre-commit_pep8 ├── pre-commit_pretty-xml ├── pre-commit_strip-eol-spaces └── pre-commit_tabs-to-spaces ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # ----------------------------------------------------------------------------- # After much research... # text should be auto, it's the only sane cross-platform option # # See: http://stackoverflow.com/a/10855862 # See also: http://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/ # # To squelch extraneous messages on windows, run: # git --config core.autocrlf=true # # To stay consistent on OS X and Linux, run: # git --config core.autocrlf=input # ----------------------------------------------------------------------------- * text eol=auto # SOA binary asset files: *.png binary *.ogg binary *.ico binary # Visual Studio files: *.pbxproj binary merge=union *.vcxproj text merge=union *.sln text merge=union # Windows binary project files: *.exe binary *.dll binary *.lib binary *.a binary *.hpp binary # Language aware diffs and force line endings if git did not know they are text: *.h text diff=cpp *.hpp text diff=cpp *.inl text diff=cpp *.cpp text diff=cpp *.py text diff=python *.cs text diff=csharp ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ x64/ build/ _build/ _clang/ _ninja/ _debug/ bld/ [Bb]in/ [Oo]bj/ *.i # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* #NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # NuGet Packages Directory packages/ ## TODO: If the tool you use requires repositories.config uncomment the next line #!packages/repositories.config # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) !packages/build/ # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ #OS junk files [Tt]humbs.db *.DS_Store desktop.ini # Python files *.pyc # A Folder To Put Personal Stuff Mine/ #VS2015 openDB *.opendb .vscode/ ================================================ FILE: .gitmodules ================================================ [submodule "game"] path = game url = https://github.com/RegrowthStudios/SoAGameData.git [submodule "Vorb"] path = Vorb url = https://github.com/RegrowthStudios/Vorb.git ================================================ FILE: .mailmap ================================================ Benjamin Arnold barnold1953 Brian Bruggeman Brian Cristian Zaloj Cristian Zaloj czaloj Frank McCoy Jesse jessenic ================================================ FILE: .travis.yml ================================================ # OSX/Linux (https://github.com/travis-ci-tester/toolchain-table) language: - cpp # Container-based infrastructure (Linux) # * https://docs.travis-ci.com/user/migrating-from-legacy/#How-can-I-use-container-based-infrastructure%3F sudo: - false # Install packages differs for container-based infrastructure # * https://docs.travis-ci.com/user/migrating-from-legacy/#How-do-I-install-APT-sources-and-packages%3F # List of available packages: # * https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-trusty # List of available sources: # * https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json addons: apt: sources: - ubuntu-toolchain-r-test packages: - python3-pip - g++-7 - g++-8 dist: - trusty env: global: #soa - secure: "LIcgKhksDL/NoBH9YpAggZMbppx53DhzUP8AkHfIH6vZ9Gq2ZuRN8KFj7SeCQX7qBJmv+vtGrW0ZdXHNXYNgEgnuqOjwdX3Z5ccAmrsuq41Qh3Hki+K3wKPKzenJxL6bF/UxTRUQipoKEP6EQS/WRkZejP7SS/YQBgmAx3eLAqx7N6LJxBgvUmKgaGAfaODjAeix7faasPMlFDVqfWn6+kbOirUEwBNxGUXJrSi1OupMwlhrNmhEoFqfRSkDbcMi5914wE126c3vG3bKSNBzCN6hSlO++knidZVaWnxwZm8Oq1SXpzLmr3YmiiWxiK5bo+LpIow00C7Iq5k3IA+otJD4NdLShU7x8jzryeI+4VQlo2UVF01Ssq/xboVJoWdF+RT2PFaKB3eJiBiXWfN2BVMr75sWj+7qb1o0KwZZtpa+qFQxuVPLKQuZ/fW6AI/ybk7NoHM3K7csPioJERlhQQ9fOTasqEOiGYCKhdjrP3Zg2sHjSlITQ68Jb+q8O/ku4taRiWprQ82/wtzMI4Yo6CdcLvtXHoXgdIPSJp/5x1/HotdSIK9n4Si6vOYNW0jtRVIc5L8neVScHEDOdhejxw0s7jArA3MMnWB6o9TS2oDv4uzPhHd+wyr86oiUT/HQq8jXUMbKSc0asFVf276b9TRTxcW7WdjveoznorDuIYE=" #krazer # - secure: "inNq/wQPDmZlK1IOwe2DPryyRZWZxg3qAJ6/KZ/zeg8/lQvkwPAzwcp9QM/zf2rzDvNBJ/ZOIwO0lcOpXlyAt6hhhSrD0T/T1HObu1F6Iv9cMy1i8fvu3RH27Kp5eIepHeGINo3uEIlyR3XZbA8Ir0jPBc2XFWkK6WyJm00wsKozlFH1PXboekClWcXL7TJWyP5FV96L9vYDa7uorFe1ct8jSkot3dTKpg4782mZNTLH+T2GgVSmkoyMzsnNwHmz11gHo+opm3LVC9sbIhjym+iwQ0CGQwdX7B6bu/v4yiHoMqBxG2d9zrB6YiG1Aa8x4KbBauc9I9NK7Xvk/e8lthCk0rcU+TXJJKciAJKVzjYkPCRsXOK/awb5YmbBsQpvWUXPztc84soCtFf6xEH6JMKJqrL76CKr30XXNJvum4MO0q8IaM7XH2+VjxxjrjU/g3WKhJZr3sXzYWqJvG7r8ZtmsohZPEUewckw5R4AXPNEjhLMIu+/M2CC86xbIWwdHrhQuM/03LDNIdqfzMINmQAewdEzJ/aF3SdVwoE94n4eeC68KjckcqRzvcPI0My3Wr1lq4ON61rQGgCNJWC2GDOYES5DK2bgoE2fJS4ULEL4R9CXnI47kFGAW6cH7StjZkWP+VqbkdEFSxuvq54mMYC7CdrTiMC2Zfm9PuAZtQY=" matrix: include: # Linux { - os: linux env: > TOOLCHAIN=clang-cxx17 PROJECT_DIR=./ - os: linux env: > TOOLCHAIN=gcc-7-cxx17 PROJECT_DIR=./ - os: linux env: > TOOLCHAIN=gcc-8-cxx17 PROJECT_DIR=./ #can not compile glew # - os: linux # env: > # TOOLCHAIN=android-ndk-r17-api-21-arm64-v8a-neon-clang-libcxx14 # PROJECT_DIR=./ # # - os: linux # env: > # TOOLCHAIN=analyze-cxx17 # PROJECT_DIR=./ # # - os: linux # env: > # TOOLCHAIN=sanitize-address-cxx17 # PROJECT_DIR=./ # # - os: linux # env: > # TOOLCHAIN=sanitize-leak-cxx17 # PROJECT_DIR=./ # # - os: linux # env: > # TOOLCHAIN=sanitize-thread-cxx17 # PROJECT_DIR=./ # } # OSX { #compile issues with templates # - os: osx # osx_image: xcode9.4 # env: > # TOOLCHAIN=osx-10-13-make-cxx14 # PROJECT_DIR=./ # # - os: osx # osx_image: xcode9.4 # env: > # TOOLCHAIN=osx-10-13-cxx14 # PROJECT_DIR=./ # #lua not packaged correctly for mac/ios # - os: osx # osx_image: xcode9.4 # env: > # TOOLCHAIN=ios-nocodesign-11-4-dep-9-3 # PROJECT_DIR=./ # } install: # Info about OS - uname -a # Info about available disk space - df -h $HOME # Disable autoupdate # * https://github.com/Homebrew/brew/blob/7d31a70373edae4d8e78d91a4cbc05324bebc3ba/Library/Homebrew/manpages/brew.1.md.erb#L202 - export HOMEBREW_NO_AUTO_UPDATE=1 # Install Python 3 - if [[ "`uname`" == "Darwin" ]]; then travis_retry brew upgrade python || echo "Ignoring failure..."; fi - if [[ "`uname`" == "Darwin" ]]; then travis_retry brew install python3; fi # Install Python package 'requests' # 'easy_install3' is not installed by 'brew install python3' on OS X 10.9 Maverick - if [[ "`uname`" == "Darwin" ]]; then pip3 install requests; fi - if [[ "`uname`" == "Darwin" ]]; then pip3 install gitpython; fi - if [[ "`uname`" == "Linux" ]]; then travis_retry pip3 install --user requests; fi - if [[ "`uname`" == "Linux" ]]; then travis_retry pip3 install --user gitpython; fi # Install latest Polly toolchains and scripts - wget https://github.com/ruslo/polly/archive/master.zip - unzip master.zip - POLLY_ROOT="`pwd`/polly-master" - export PATH="${POLLY_ROOT}/bin:${PATH}" # Install dependencies (CMake, Android NDK) - install-ci-dependencies.py --prune-archives # Tune locations - export PATH="`pwd`/_ci/cmake/bin:${PATH}" # Installed if toolchain is Android (otherwise directory doesn't exist) - export ANDROID_NDK_r10e="`pwd`/_ci/android-ndk-r10e" - export ANDROID_NDK_r11c="`pwd`/_ci/android-ndk-r11c" - export ANDROID_NDK_r15c="`pwd`/_ci/android-ndk-r15c" - export ANDROID_NDK_r16b="`pwd`/_ci/android-ndk-r16b" - export ANDROID_NDK_r17="`pwd`/_ci/android-ndk-r17" script: - python3 ./jenkins.py # https://docs.travis-ci.com/user/customizing-the-build/#Whitelisting-or-blacklisting-branches branches: except: - /^pr\..*/ - /^v[0-9]+\.[0-9]+\.[0-9]+$/ git: submodules: false before_script: git submodule update --init Vorb ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required (VERSION 3.0) set(HUNTER_STATUS_DEBUG ON) #setup cache server and suppot upload set( HUNTER_CACHE_SERVERS "https://github.com/huntercache/SoA" CACHE STRING "Default cache server" ) string(COMPARE EQUAL "$ENV{TRAVIS}" "true" is_travis) string(COMPARE EQUAL "$ENV{APPVEYOR}" "True" is_appveyor) string(COMPARE EQUAL "$ENV{GITHUB_USER_PASSWORD}" "" password_is_empty) if((is_travis OR is_appveyor) AND NOT password_is_empty) option(HUNTER_RUN_UPLOAD "Upload cache binaries" ON) endif() message(STATUS "Travis: ${is_travis}") message(STATUS "Appveyor: ${is_appveyor}") message(STATUS "Password empty: ${password_is_empty}") message(STATUS "Hunter upload: ${HUNTER_RUN_UPLOAD}") set( HUNTER_PASSWORDS_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/hunter/passwords.cmake" CACHE FILEPATH "Hunter passwords" ) include(${CMAKE_CURRENT_LIST_DIR}/cmake/hunter/HunterGate.cmake) HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.23.222.tar.gz" SHA1 "0b88baaa2a9a35b8ce632c57ade66be8dd918afd" ) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) project(SoA) #if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # if(MSVC_VERSION GREATER 1900) # message(FATAL_ERROR "Only Visual Studio 2015 and below are currently supported.") # endif() #endif() add_subdirectory(Vorb) add_subdirectory(SoA) ================================================ FILE: LICENSE.txt ================================================ Copyright (c) 2018 Regrowth Studios 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. ================================================ FILE: ModTest/ModTest.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {C49943E6-5645-4D1C-A5F6-FE2D7C94D924} ModTest DynamicLibrary true v140 MultiByte DynamicLibrary true v140 MultiByte DynamicLibrary false v140 true MultiByte DynamicLibrary false v140 true MultiByte $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\ModTest\ mod $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\ModTest\ mod $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\ModTest\ mod $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\ModTest\ mod Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true true true ================================================ FILE: ModTest/ModTest.vcxproj.filters ================================================  ================================================ FILE: ModTest/main.cpp ================================================ #include #define _WINSOCKAPI_ #include BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { // Perform actions based on the reason for calling. switch (fdwReason) { case DLL_PROCESS_ATTACH: // Initialize once for each new process. puts("Mod Attach"); fprintf(stderr, "DLL"); break; case DLL_THREAD_ATTACH: // Do thread-specific initialization. puts("Mod Thread Attach"); break; case DLL_THREAD_DETACH: // Do thread-specific cleanup. puts("Mod Thread Detach"); break; case DLL_PROCESS_DETACH: // Perform any necessary cleanup. puts("Mod Detach"); break; } return TRUE; } extern "C" __declspec(dllexport) int getCode() { return 42; } ================================================ FILE: ProjectConverter/App.Designer.cs ================================================ namespace ProjectConverter { partial class App { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if(disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.comboBox1 = new System.Windows.Forms.ComboBox(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.checkBox1 = new System.Windows.Forms.CheckBox(); this.log = new System.Windows.Forms.RichTextBox(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.files = new System.Windows.Forms.ListBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.button1 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button(); this.groupBox1.SuspendLayout(); this.groupBox2.SuspendLayout(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); this.tabPage2.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // comboBox1 // this.comboBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.comboBox1.FormattingEnabled = true; this.comboBox1.Items.AddRange(new object[] { "None"}); this.comboBox1.Location = new System.Drawing.Point(3, 16); this.comboBox1.Name = "comboBox1"; this.comboBox1.Size = new System.Drawing.Size(385, 21); this.comboBox1.TabIndex = 0; this.comboBox1.Text = "None"; this.comboBox1.DropDown += new System.EventHandler(this.comboBox1_DropDown); this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); // // groupBox1 // this.groupBox1.Controls.Add(this.comboBox1); this.groupBox1.Dock = System.Windows.Forms.DockStyle.Top; this.groupBox1.Location = new System.Drawing.Point(0, 0); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(391, 43); this.groupBox1.TabIndex = 1; this.groupBox1.TabStop = false; this.groupBox1.Text = "VS Processes"; // // groupBox2 // this.groupBox2.Controls.Add(this.checkBox1); this.groupBox2.Dock = System.Windows.Forms.DockStyle.Bottom; this.groupBox2.Location = new System.Drawing.Point(0, 317); this.groupBox2.Name = "groupBox2"; this.groupBox2.Size = new System.Drawing.Size(391, 51); this.groupBox2.TabIndex = 2; this.groupBox2.TabStop = false; this.groupBox2.Text = "Options"; // // checkBox1 // this.checkBox1.AutoSize = true; this.checkBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.checkBox1.Location = new System.Drawing.Point(3, 16); this.checkBox1.Name = "checkBox1"; this.checkBox1.Size = new System.Drawing.Size(385, 32); this.checkBox1.TabIndex = 0; this.checkBox1.Text = "Close On Process Termination"; this.checkBox1.UseVisualStyleBackColor = true; this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); // // log // this.log.Dock = System.Windows.Forms.DockStyle.Fill; this.log.Location = new System.Drawing.Point(3, 3); this.log.Name = "log"; this.log.Size = new System.Drawing.Size(377, 242); this.log.TabIndex = 3; this.log.Text = ""; // // tabControl1 // this.tabControl1.Controls.Add(this.tabPage1); this.tabControl1.Controls.Add(this.tabPage2); this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl1.Location = new System.Drawing.Point(0, 43); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; this.tabControl1.Size = new System.Drawing.Size(391, 274); this.tabControl1.TabIndex = 4; // // tabPage1 // this.tabPage1.Controls.Add(this.log); this.tabPage1.Location = new System.Drawing.Point(4, 22); this.tabPage1.Name = "tabPage1"; this.tabPage1.Padding = new System.Windows.Forms.Padding(3); this.tabPage1.Size = new System.Drawing.Size(383, 248); this.tabPage1.TabIndex = 0; this.tabPage1.Text = "Log"; this.tabPage1.UseVisualStyleBackColor = true; // // tabPage2 // this.tabPage2.Controls.Add(this.tableLayoutPanel1); this.tabPage2.Controls.Add(this.files); this.tabPage2.Location = new System.Drawing.Point(4, 22); this.tabPage2.Name = "tabPage2"; this.tabPage2.Padding = new System.Windows.Forms.Padding(3); this.tabPage2.Size = new System.Drawing.Size(383, 248); this.tabPage2.TabIndex = 1; this.tabPage2.Text = "Files"; this.tabPage2.UseVisualStyleBackColor = true; // // files // this.files.Dock = System.Windows.Forms.DockStyle.Fill; this.files.FormattingEnabled = true; this.files.Location = new System.Drawing.Point(3, 3); this.files.Name = "files"; this.files.Size = new System.Drawing.Size(377, 242); this.files.TabIndex = 0; // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 2; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.Controls.Add(this.button2, 1, 0); this.tableLayoutPanel1.Controls.Add(this.button1, 0, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom; this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 192); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 1; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(377, 53); this.tableLayoutPanel1.TabIndex = 1; // // button1 // this.button1.Dock = System.Windows.Forms.DockStyle.Fill; this.button1.Location = new System.Drawing.Point(3, 3); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(182, 47); this.button1.TabIndex = 0; this.button1.Text = "Add"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // button2 // this.button2.Dock = System.Windows.Forms.DockStyle.Fill; this.button2.Location = new System.Drawing.Point(191, 3); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(183, 47); this.button2.TabIndex = 1; this.button2.Text = "Remove"; this.button2.UseVisualStyleBackColor = true; this.button2.Click += new System.EventHandler(this.button2_Click); // // App // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(391, 368); this.Controls.Add(this.tabControl1); this.Controls.Add(this.groupBox2); this.Controls.Add(this.groupBox1); this.Name = "App"; this.Text = "VS File Changer"; this.groupBox1.ResumeLayout(false); this.groupBox2.ResumeLayout(false); this.groupBox2.PerformLayout(); this.tabControl1.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.tabPage2.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox comboBox1; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.RichTextBox log; private System.Windows.Forms.CheckBox checkBox1; private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.ListBox files; private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button1; } } ================================================ FILE: ProjectConverter/App.config ================================================ ================================================ FILE: ProjectConverter/App.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Diagnostics; using System.Threading; using System.IO; namespace ProjectConverter { public partial class App : Form { const int NO_PROCESS = 0; Process[] vsProcesses; Process vsProcess; Thread tProcessTermination, tProcessPoll; Dictionary> trackedFiles; public App() { InitializeComponent(); trackedFiles = new Dictionary>(); trackedFiles[NO_PROCESS] = new List(); log.AppendText("Please Select A List Of Files To Track\n"); log.AppendText("Select A Process To Track, If Necessary\n"); } void RebuildProcessList() { // Find Processes var processes = Process.GetProcesses(); vsProcesses = processes.Where((p) => { return p.ProcessName.Equals("devenv"); }).ToArray(); // Add Processes comboBox1.Items.Clear(); comboBox1.Items.Add("None"); if(vsProcesses.Length > 0) { foreach(var p in vsProcesses) { comboBox1.Items.Add(p.MainWindowTitle); } } } List GetTrackMap() { List l; int id = vsProcess == null ? NO_PROCESS : vsProcess.Id; if(!trackedFiles.TryGetValue(id, out l)) { l = new List(); trackedFiles[id] = l; } return l; } void VSProcessWaiter() { vsProcess.WaitForExit(); if(checkBox1.Checked) { Process.GetCurrentProcess().Kill(); } else { vsProcess = null; Invoke((MethodInvoker)delegate { RebuildProcessList(); comboBox1.SelectedIndex = 0; }); } } void VSProcessPoller() { while(true) { var remoteFiles = GetTrackMap(); if(remoteFiles.Count != 0) { var files = new FileInfo[remoteFiles.Count]; remoteFiles.CopyTo(files); foreach(var fi in remoteFiles) { Convert(fi); } } Thread.Sleep(2000); } } void CreateProcessThreads() { if(vsProcess == null) return; tProcessTermination = new Thread(VSProcessWaiter); tProcessTermination.Priority = ThreadPriority.BelowNormal; tProcessTermination.Start(); tProcessPoll = new Thread(VSProcessPoller); tProcessPoll.Priority = ThreadPriority.BelowNormal; tProcessPoll.Start(); } void TerminateProcessThreads() { if(tProcessTermination != null && tProcessTermination.ThreadState == System.Threading.ThreadState.Running) { tProcessTermination.Abort(); tProcessTermination = null; } if(tProcessPoll != null && tProcessPoll.ThreadState == System.Threading.ThreadState.Running) { tProcessPoll.Abort(); tProcessPoll = null; } } void Convert(FileInfo fi) { string data; try { using(var s = fi.OpenRead()) { data = new StreamReader(s).ReadToEnd(); } Regex rgx = new Regex(@"<(?\w+) Include=(?[^>]+)>\s+<(?\w+)>(?[^<]+)>\s+>"); if(rgx.Match(data).Success) { data = rgx.Replace(data, @"<${Outer} Include=${Name}><${Inner}>${Data}" ); using(var s = fi.Create()) { var w = new StreamWriter(s); w.Write(data); w.Flush(); } Invoke((MethodInvoker)delegate { log.AppendText("Modified: " + fi.FullName + "\n"); }); } } catch(Exception e) { Invoke((MethodInvoker)delegate { log.AppendText("Error: " + fi.FullName + "\n"); log.AppendText(e.Message + "\n"); }); } return; } // Process Selection private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { TerminateProcessThreads(); files.Items.Clear(); files.Items.AddRange((from fi in GetTrackMap() select fi.FullName).ToArray()); if(comboBox1.SelectedIndex == 0) { vsProcess = null; } else { vsProcess = vsProcesses[comboBox1.SelectedIndex - 1]; CreateProcessThreads(); } } // Process Refresh void comboBox1_DropDown(object sender, System.EventArgs e) { RebuildProcessList(); } // Auto-Shutdown Detection private void checkBox1_CheckedChanged(object sender, EventArgs e) { if(checkBox1.Checked) { if(tProcessTermination == null && vsProcess != null) { CreateProcessThreads(); } } else { TerminateProcessThreads(); } } // Add Tracked File private void button1_Click(object sender, EventArgs e) { var l = GetTrackMap(); using(var fd = new OpenFileDialog()) { fd.ShowDialog(); foreach(var f in fd.FileNames) { FileInfo fi = new FileInfo(f); if(!l.Contains(fi, new FileNameComparer())) { l.Add(fi); } } } files.Items.Clear(); files.Items.AddRange((from fi in l select fi.FullName).ToArray()); } // Removed Tracked File private void button2_Click(object sender, EventArgs e) { var l = GetTrackMap(); foreach(var file in files.SelectedItems) { FileInfo fi = new FileInfo(file.ToString()); l.RemoveAll((f) => { return f.FullName.Equals(fi.FullName); }); } files.Items.Clear(); files.Items.AddRange((from fi in l select fi.FullName).ToArray()); } class FileNameComparer : IEqualityComparer { public bool Equals(FileInfo x, FileInfo y) { return x.FullName.Equals(y.FullName); } public int GetHashCode(FileInfo obj) { return obj.FullName.GetHashCode(); } } [STAThread] static void Main(string[] args) { using(App app = new App()) { app.ShowDialog(); } } } } ================================================ FILE: ProjectConverter/App.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 True True ================================================ FILE: ProjectConverter/ProjectConverter.csproj ================================================  Debug AnyCPU {75075CEE-B74F-4E21-8C88-D64ACD182237} WinExe Properties ProjectConverter ProjectConverter v4.0 512 Client AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ProjectConverter.App Form App.cs App.cs ================================================ FILE: ProjectConverter/ProjectConverter.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectConverter", "ProjectConverter.csproj", "{75075CEE-B74F-4E21-8C88-D64ACD182237}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {75075CEE-B74F-4E21-8C88-D64ACD182237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75075CEE-B74F-4E21-8C88-D64ACD182237}.Debug|Any CPU.Build.0 = Debug|Any CPU {75075CEE-B74F-4E21-8C88-D64ACD182237}.Release|Any CPU.ActiveCfg = Release|Any CPU {75075CEE-B74F-4E21-8C88-D64ACD182237}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: ProjectConverter/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("ProjectConverter")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ProjectConverter")] [assembly: AssemblyCopyright("Copyright © 2014")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("e74470a1-41b5-431f-9d38-5ebd4d57c105")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: README.md ================================================ [![dicord](https://img.shields.io/discord/459062989094649866.svg?logo=discord "Discord")](https://discord.gg/QRex8GK) ![travis](https://img.shields.io/travis/RegrowthStudios/SoACode-Public/develop.svg?style=flat-square&label=Linux "Travis CI") ![appveyor](https://img.shields.io/appveyor/ci/SamThePsychoticLeprechaun/soacode-public/develop.svg?style=flat-square&label=Windows "AppVeyor CI") # Seed of Andromeda This repository contains the source code for Seed of Andromeda. ## Getting Started This guide will walk you through setting up as a contributor to the Seed of Andromeda project. There is a basic requirement of having several packages installed prior to being able to develop. We support all three major operating systems: Windows, Mac and Linux. ### Contributing Before beginning your SoA journey, please take a moment to use the following resources to get an idea of how to contribute, what you might be able to contribute to specifically, and to meet some of the other contributors. * [Wiki](https://github.com/RegrowthStudios/SoACode-Public/wiki) * [Issues](https://github.com/RegrowthStudios/SoACode-Public/issues) * [Discord](https://discord.gg/QRex8GK) ### Setting Up: **IMPORTANT**: Before following any of the instructions linked below for the platforms we support, please do take a second to fork the repository! If you are new to GitHub, you can do so by clicking the "fork" button on the top right of this page. If you have cloned the repository before forking, no worries! We can fix it, by following [these instructions](#fixing-a-pre-fork-clone). Now we're forked, follow the link to the section on setting up for your OS of choice: * [Windows](#windows) * [Mac](#mac) * [Linux](#linux) ### Building: Now you have a copy of the code, and perhaps have played with it a little, why not give it a whirl? * [Building](#building-1) ## Setting Up ### Windows #### Prerequisites * Compiler: [Microsoft Visual Studio 2015 (only)](https://visualstudio.microsoft.com/) * Software Version Control: [Git](http://git-scm.com/downloads) * (Optional) MSVS SVC Plugin: [MSVS Git Plugin](http://msdn.microsoft.com/en-us/library/hh850437.aspx) * (Optional) TortoiseGit: [tortoisegit](https://tortoisegit.org/download) ### Mac #### Prerequisites * Compiler: [Xcode](https://developer.apple.com/xcode/) * Software Version Control: [Git](http://git-scm.com/downloads) Optionally, with [Homebrew](http://brew.sh/): ```brew install git``` * Preferred editor: [Sublime Text](http://www.sublimetext.com/) and optional packages * [PackageControl](https://sublime.wbond.net/installation) * [CMake](https://sublime.wbond.net/packages/CMake) - CMake.tmLanguage * [GitGutter](https://sublime.wbond.net/packages/GitGutter) - A Sublime Text 2/3 plugin to see git diff in gutter * [SublimeCodeIntel](https://sublime.wbond.net/packages/SublimeCodeIntel) - Full-featured code intelligence and smart autocomplete engine * [SublimeLinter](https://sublime.wbond.net/packages/SublimeLinter) -- Interactive code linting framework for Sublime Text 3 * [SublimeLinter-cpplint](https://sublime.wbond.net/packages/SublimeLinter-cpplint) -- This linter plugin for SublimeLinter provides an interface to cpplint. * [SublimeLinter-pep8](https://sublime.wbond.net/packages/SublimeLinter-pep8) - SublimeLinter plugin for python, using pep8. * [SublimeLinter-contrib-clang](https://sublime.wbond.net/packages/SublimeLinter-contrib-clang) - https://sublime.wbond.net/packages/SublimeLinter-contrib-clang ### Linux #### Prerequisites * Compiler: gcc or clang * Install per your preferred operating system package control... * Portage: ```bash sudo emerge -DuNqa gcc # for gcc sudo emerge -DuNqa clang # for clang ``` * PacMan: ```bash sudo pacman -S gcc sudo pacman -S clang ``` * Apt: ```bash sudo apt-get install gcc sudo apt-get install clang ``` * Yum: ```bash sudo yum install gcc sudo yum install clang ``` * Software Version Control: [Git](http://git-scm.com/downloads) * Portage: ```bash sudo emerge -DuNqa git ``` * PacMan: ```bash sudo pacman -S git ``` * Apt: ```bash sudo apt-get install git ``` * Yum: ```bash sudo yum install git ``` # Setup 1. Create a folder to hold the repositories and change to directory * Windows ```cmd Windows + R cmd cd c:\ mkdir -p repos cd c:\repos ``` * Linux ```bash mkdir ~/repos cd ~/repos ``` * Mac ```bash cmd + space Terminal mkdir ~/repos cd ~/repos ``` 2. Clone the Seed of Andromeda repositories ```cmd git clone --recurse-submodules https://github.com/YOUR_GITHUB_NAME/SoACode-Public.git soa ``` 3. Change to soa direcotory * Windows ``` cd c:\repos\soa ``` * Linux & Mac ``` cd ~/repos/soa ``` 4. (optional) Do this step only if you plan to fork your own Vorb or SoAGameData repos. * Fork both the [Vorb](https://github.com/RegrowthStudios/Vorb) and/or [GameData](https://github.com/RegrowthStudios/SoAGameData) repos in github. * Set origin of submodules to your forked repositories ```cmd # Assuming we're already inside the top-level directory of your SoACode-Public repository. cd Vorb git remote set-url origin https://github.com/YOUR_GITHUB_NAME/Vorb.git cd game git remote set-url origin https://github.com/YOUR_GITHUB_NAME/SoAGameData.git ``` # Building 1. Pull latest code (from inside .../repos/soa) ```bash git checkout develop # or your current branch git pull --recurse-submodules ``` 2. Run the build script (--help for options) * Windows: ```cmd build.bat # or compile from within your Visual Studio environment ``` * Linux: ```bash ./build.sh ``` 3. Run the built executable * Windows: ```cmd build/SoA/launch-soa-{Release|Debug}.cmd # or launch from within your Visual Studio environment ``` * Linux: ```bash ./build/SoA/launch-soa.sh ``` # Fixing a Pre-Fork Clone So, you've accidentally cloned the repository before forking it, eh? No problem. Just run the following git commands inside of the repository and everything will be as it should be! Firstly, if you still haven't, fork the repositories you want to contribute to! Now you have a fork we want to set `origin` of each of your local repositories (which is the default remote repository to push changes to) to your corresponding forked repositories: ```bash # Assuming we're already inside the top-level directory of your SoACode-Public repository. git remote set-url origin https://github.com/YOUR_GITHUB_NAME/SoACode-Public.git cd Vorb git remote set-url origin https://github.com/YOUR_GITHUB_NAME/Vorb.git cd game git remote set-url origin https://github.com/YOUR_GITHUB_NAME/SoAGameData.git ``` If you haven't forked, either Vorb or SoAGameData as you don't intend to contribute to that repository, then you don't need to do run the commands corresponding to that repository. That's it! It's all fixed. :) ================================================ FILE: SoA/AABBCollidableComponentUpdater.cpp ================================================ #include "stdafx.h" #include "AABBCollidableComponentUpdater.h" #include "GameSystem.h" #include "SpaceSystem.h" #include "BlockPack.h" #include "VoxelSpaceConversions.h" void AABBCollidableComponentUpdater::update(GameSystem* gameSystem, SpaceSystem* spaceSystem) { for (auto& it : gameSystem->aabbCollidable) { collideWithVoxels(it.second, gameSystem, spaceSystem); } } void AABBCollidableComponentUpdater::collideWithVoxels(AabbCollidableComponent& cmp, GameSystem* gameSystem, SpaceSystem* spaceSystem) { // Clear old data cmp.voxelCollisions.clear(); // Get needed components auto& physics = gameSystem->physics.get(cmp.physics); auto& position = gameSystem->voxelPosition.get(physics.voxelPosition); if (position.parentVoxel == 0) return; auto& sphericalVoxel = spaceSystem->sphericalVoxel.get(position.parentVoxel); f64v3 vpos = position.gridPosition.pos + f64v3(cmp.offset - cmp.box * 0.5f); i32v3 vp(glm::floor(vpos)); i32v3 bounds(glm::ceil(f64v3(cmp.box) + glm::fract(vpos))); ChunkGrid& grid = sphericalVoxel.chunkGrids[position.gridPosition.face]; const BlockPack* bp = sphericalVoxel.blockPack; std::map> boundedVoxels; // Get bounded voxels for (int yi = 0; yi < bounds.y; yi++) { for (int zi = 0; zi < bounds.z; zi++) { for (int xi = 0; xi < bounds.x; xi++) { i32v3 p = vp + i32v3(xi, yi, zi); i32v3 cpos = VoxelSpaceConversions::voxelToChunk(p); // TODO(Ben): Negative numbers? ChunkID chunkID(cpos); i32v3 cp = p - cpos * CHUNK_WIDTH; boundedVoxels[chunkID].push_back(cp.y * CHUNK_LAYER + cp.z * CHUNK_WIDTH + cp.x); } } } // Find Collidable voxels for (auto& it : boundedVoxels) { ChunkHandle chunk = grid.accessor.acquire(it.first); if (chunk->genLevel == GEN_DONE) { std::lock_guard l(chunk->dataMutex); for (auto& i : it.second) { BlockID id = chunk->blocks.get(i); if (bp->operator[](id).collide) { // TODO(Ben): Don't need to look up every time. cmp.voxelCollisions[it.first].emplace_back(id, i); } } } chunk.release(); } // Helper macro for below code #define CHECK_CODE(dir) \ /* Acquire new chunk if needed */ \ if (id != currentID) { \ /* Release current chunk */ \ if (chunk.isAquired()) { \ chunk->dataMutex.unlock(); \ chunk.release(); \ } \ chunk = grid.accessor.acquire(id); \ if (chunk->genLevel != GEN_DONE) { \ chunk.release(); \ currentID = ChunkID(0xffffffffffffffffu); \ } else { \ chunk->dataMutex.lock(); \ currentID = id; \ /* Check the voxel */ \ if (chunk->genLevel == GEN_DONE && bp->operator[](chunk->blocks.get(index)).collide) { \ cd.dir = true; \ } \ } \ } else {\ /* Check the voxel */ \ if (chunk->genLevel == GEN_DONE && bp->operator[](chunk->blocks.get(index)).collide) { \ cd.dir = true; \ } \ } // Set neighbor collide flags // TODO(Ben): More than top ChunkID currentID(0xffffffffffffffffu); ChunkHandle chunk; for (auto& it : cmp.voxelCollisions) { for (auto& cd : it.second) { { // Left ChunkID id = it.first; int index = (int)cd.index; if ((index & 0x1f) == 0) { index += CHUNK_WIDTH_M1; id.x--; } else { index--; } CHECK_CODE(left); } { // Right ChunkID id = it.first; int index = (int)cd.index; if ((index & 0x1f) == CHUNK_WIDTH_M1) { index -= CHUNK_WIDTH_M1; id.x++; } else { index++; } CHECK_CODE(right); } { // Bottom ChunkID id = it.first; int index = (int)cd.index; if (index < CHUNK_LAYER) { index += (CHUNK_SIZE - CHUNK_LAYER); id.y--; } else { index -= CHUNK_LAYER; } CHECK_CODE(bottom); } { // Top ChunkID id = it.first; int index = (int)cd.index; if (index >= CHUNK_SIZE - CHUNK_LAYER) { index -= (CHUNK_SIZE - CHUNK_LAYER); id.y++; } else { index += CHUNK_LAYER; } CHECK_CODE(top); } { // Back ChunkID id = it.first; int index = (int)cd.index; if ((index & 0x3ff) / CHUNK_WIDTH == 0) { index += (CHUNK_LAYER - CHUNK_WIDTH); id.z--; } else { index -= CHUNK_WIDTH_M1; } CHECK_CODE(back); } { // Front ChunkID id = it.first; int index = (int)cd.index; if ((index & 0x3ff) / CHUNK_WIDTH == CHUNK_WIDTH_M1) { index -= (CHUNK_LAYER - CHUNK_WIDTH); id.z++; } else { index += CHUNK_WIDTH_M1; } CHECK_CODE(front); } } } // Release chunk if needed if (chunk.isAquired()) { chunk->dataMutex.unlock(); chunk.release(); } #undef CHECK_CODE } ================================================ FILE: SoA/AABBCollidableComponentUpdater.h ================================================ // // AABBCollidableComponentUpdater.h // Seed of Andromeda // // Created by Benjamin Arnold on 30 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Updater for AABB components. // #pragma once #ifndef AABBCollidableComponentUpdater_h__ #define AABBCollidableComponentUpdater_h__ #include class GameSystem; class SpaceSystem; struct AabbCollidableComponent; class AABBCollidableComponentUpdater { public: void update(GameSystem* gameSystem, SpaceSystem* spaceSystem); private: void collideWithVoxels(AabbCollidableComponent& cmp, GameSystem* gameSystem, SpaceSystem* spaceSystem); }; #endif // AABBCollidableComponentUpdater_h__ ================================================ FILE: SoA/ARProcessor.cpp ================================================ #include "stdafx.h" #include "ARProcessor.h" ================================================ FILE: SoA/ARProcessor.h ================================================ /// /// ARProcessor.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 9 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Stores and keeps track of Augmented Reality information /// #pragma once #ifndef ARProcessor_h__ #define ARProcessor_h__ class ARProcessor { }; #endif // ARProcessor_h__ ================================================ FILE: SoA/AmbienceLibrary.cpp ================================================ #include "stdafx.h" #include "AmbienceLibrary.h" void AmbienceLibrary::addTrack(const nString& list, const nString& name, const vpath& file) { m_lists[list][name] = file; } const AmbienceList& AmbienceLibrary::getTracks(const nString& name) const { return m_lists.at(name); } AmbienceLibrary::Track AmbienceLibrary::getTrack(const nString& list, Random& rGen) const { const AmbienceList& tracks = getTracks(list); ui32 trackNum = (ui32)(rGen.genMT() * tracks.size()) % tracks.size(); printf("Chose track %d out of %zd in %s\n", trackNum + 1, tracks.size(), list.c_str()); AmbienceList::const_iterator iter = tracks.cbegin(); while (trackNum != 0) { iter++; trackNum--; } return Track(iter->first, iter->second); } ================================================ FILE: SoA/AmbienceLibrary.h ================================================ /// /// AmbienceLibrary.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Library of all ambience tracks /// #pragma once #ifndef AmbienceLibrary_h__ #define AmbienceLibrary_h__ #include #include typedef std::unordered_map AmbienceList; ///< List of tracks (name, file) for an ambience type /// Library of music tracks organized by ambience class AmbienceLibrary { public: typedef std::pair Track; ///< A named music file /// Add a track to the library /// @param list: Ambience type /// @param name: Name of the track /// @param file: File for the track void addTrack(const nString& list, const nString& name, const vpath& file); /// Obtain a list of tracks for a certain type of ambience /// @param name: Ambience type /// @return The list of tracks const AmbienceList& getTracks(const nString& name) const; /// Obtain a random track in an ambient list /// @param list: Ambience type /// @param rGen: Random generator /// @return A random track that can be played Track getTrack(const nString& list, Random& rGen) const; private: std::unordered_map m_lists; ///< Lists of ambiences that may play }; #endif // AmbienceLibrary_h__ ================================================ FILE: SoA/AmbiencePlayer.cpp ================================================ #include "stdafx.h" #include "AmbiencePlayer.h" #include #include "AmbienceLibrary.h" #define AMBIENCE_PLAYER_DISPOSAL_RATE 2.0f void AmbiencePlayer::init(vsound::Engine* engine, const AmbienceLibrary* library) { m_engine = engine; m_lib = library; } void AmbiencePlayer::dispose() { m_engine = nullptr; m_lib = nullptr; } void AmbiencePlayer::setToTrack(const nString& name, const f32& fadeTime) { // Set current ambience currentAmbience = name; if (!currentAmbience.empty()) { // Make one track come alive auto track = m_streams.find(currentAmbience); if (track != m_streams.end()) { // Reset track to be alive track->second.stream.setPeakTime(fadeTime); } else { // Add a new stream SoundStream stream; stream.stream.setPeakTime(fadeTime); m_streams[name] = stream; } } // Make all other tracks fade away for (auto& kvp : m_streams) { if (kvp.first != currentAmbience) { kvp.second.stream.setDeathTime(fadeTime); } } } void AmbiencePlayer::update(const f32& dt) { if (!m_engine || !m_lib) return; m_timerDisposal += dt; // Update volumes if necessary for (auto& kvp : m_streams) { SoundStream& stream = kvp.second; bool soundChanged = stream.stream.update(dt); if (stream.stream.isAlive()) { if (!stream.resource.isNull() && !stream.instance.isPlaying()) { // Delete a finished track m_engine->freeSound(stream.resource); } if (!stream.stream.isDying() && stream.resource.isNull()) { // Get a new track AmbienceLibrary::Track track = m_lib->getTrack(currentAmbience, m_rand); currentTrack = track.first; // Load and play the track stream.resource = m_engine->loadSound(track.second, false, true); stream.instance = m_engine->createInstance(stream.resource); stream.instance.setLooped(false); stream.instance.setVolume(stream.stream.getVolume() * m_volume); stream.instance.play(); } // Update volume if (soundChanged) { stream.instance.setVolume(stream.stream.getVolume() * m_volume); } } } // Dispose of old streams if (m_timerDisposal > AMBIENCE_PLAYER_DISPOSAL_RATE) { m_timerDisposal = 0.0f; StreamMap updated; for (auto& kvp : m_streams) { if (kvp.second.stream.isAlive()) { updated[kvp.first] = kvp.second; } else { if (!kvp.second.resource.isNull()) { m_engine->freeSound(kvp.second.resource); } } } m_streams.swap(updated); } } void AmbiencePlayer::setVolume(f32 volume) { m_volume = volume; // Update volumes if necessary for (auto& kvp : m_streams) { SoundStream& stream = kvp.second; if (stream.stream.isAlive()) { stream.instance.setVolume(stream.stream.getVolume() * m_volume); } } } ================================================ FILE: SoA/AmbiencePlayer.h ================================================ /// /// AmbiencePlayer.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Plays ambience tracks with cross-fading /// #pragma once #ifndef AmbiencePlayer_h__ #define AmbiencePlayer_h__ #include #include #include #include #include "AmbienceStream.h" DECL_VSOUND(class Engine) class AmbienceLibrary; /// Mixes ambient music for a game /// TODO: Make this thread-safe and put it on another thread with begin/end class AmbiencePlayer { public: /// Setup all player references /// @param engine: Sound engine that will be used by this player /// @param library: Reference to library of ambient songs void init(vsound::Engine* engine, const AmbienceLibrary* library); /// Destroy all streams held by the player void dispose(); /// Play a new mix of ambient music /// @param name: Name of the ambience (that must be found in the library) or "" to stop all music /// @param fadeTime: Amount of time until this stream becomes dominant void setToTrack(const nString& name, UNIT_SPACE(SECONDS) const f32& fadeTime); /// Update streams, loading in music tracks as necessary /// @param dt: Elapsed time since the last update /// TODO: return volume-change bool to allow intelligent sleeping void update(UNIT_SPACE(SECONDS) const f32& dt); const f32& getVolume() const { return m_volume; } void setVolume(f32 volume); private: /// A stream with a controller and sound information struct SoundStream { public: AmbienceStream stream; ///< Stream controller vsound::Resource resource; ///< Sound's resource data vsound::Instance instance; ///< Playing sound handle }; typedef std::unordered_map StreamMap; UNIT_SPACE(SECONDS) f32 m_timerDisposal = 0.0f; ///< Time until dead streams are destroyed vsound::Engine* m_engine = nullptr; ///< Reference to the sound engine const AmbienceLibrary* m_lib = nullptr; ///< Reference to library of ambient sounds StreamMap m_streams; ///< Currently playing ambience streams Random m_rand; ///< Random number generator f32 m_volume = 1.0f; nString currentAmbience = ""; ///< Current ambience type nString currentTrack = ""; ///< Currently playing track in the ambience stream }; #endif // AmbiencePlayer_h__ ================================================ FILE: SoA/AmbienceStream.cpp ================================================ #include "stdafx.h" #include "AmbienceStream.h" const f32& AmbienceStream::getVolume() const { return m_ratio; } bool AmbienceStream::isAlive() const { return m_ratio > 0.0f || m_change > 0.0f; } bool AmbienceStream::isDying() const { return m_change < 0.0f; } void AmbienceStream::setDeathTime(const f32& seconds) { m_change = -m_ratio / seconds; } void AmbienceStream::setPeakTime(const f32& seconds) { m_change = (1.0f - m_ratio) / seconds; } bool AmbienceStream::update(const f32& dt) { if (m_change == 0.0f) return false; m_ratio += m_change * dt; if (m_change > 0.0f) { if (m_ratio >= 1.0f) { m_ratio = 1.0f; m_change = 0.0f; } } else { if (m_ratio <= 0.0f) { m_ratio = 0.0f; m_change = 0.0f; } } return true; } ================================================ FILE: SoA/AmbienceStream.h ================================================ /// /// AmbienceStream.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// A stream of music created from a list of ambient music /// #pragma once #ifndef AmbienceStream_h__ #define AmbienceStream_h__ #include "Vorb/types.h" /// Encapsulates a stream of ambient music class AmbienceStream { public: /// @return The relative volume of the stream [0,1] const f32& getVolume() const; /// @return True if this stream is in a playing state bool isAlive() const; /// @return True if this stream is fading away bool isDying() const; /// Set this stream to fade away to nothingness /// @param seconds: Time until this stream is dead void setDeathTime(UNIT_SPACE(SECONDS) const f32& seconds); /// Set this stream to come alive from its current state /// @param seconds: Time until this stream is fully audible void setPeakTime(UNIT_SPACE(SECONDS) const f32& seconds); /// Update a stream of music /// @param dt: Elapsed time /// @return True if the volume changed bool update(UNIT_SPACE(SECONDS) const f32& dt); private: f32 m_ratio = 0.0f; ///< The current liveliness level of the stream [0,1] f32 m_change = 0.0f; ///< The rate of change of the ratio per second }; #endif // AmbienceStream_h__ ================================================ FILE: SoA/Animation.h ================================================ #pragma once #ifndef Animation_h_ #define Animation_h_ #include struct Animation { Animation() : fadeOutBegin(INT_MAX) {} int duration; int fadeOutBegin; int repetitions; int xFrames; int yFrames; int frames; vg::Texture texture; }; #endif // Animation_h_ ================================================ FILE: SoA/App.cpp ================================================ #include "stdafx.h" #include "App.h" #include #include #include #include #include "ChunkMeshManager.h" #include "CommonState.h" #include "DebugRenderer.h" #include "DevScreen.h" #include "GameManager.h" #include "GameplayLoadScreen.h" #include "GamePlayScreen.h" #include "InitScreen.h" #include "MainMenuLoadScreen.h" #include "MainMenuScreen.h" #include "SoaEngine.h" #include "SoaOptions.h" #include "SoAState.h" #include "SpaceSystem.h" #include "TestBiomeScreen.h" #include "TestBlockViewScreen.h" #include "TestConnectedTextureScreen.h" #include "TestConsoleScreen.h" #include "TestDeferredScreen.h" #include "TestDisplacementMappingScreen.h" #include "TestGasGiantScreen.h" #include "TestMappingScreen.h" #include "TestNewBlockAPIScreen.h" #include "TestNoiseScreen.h" #include "TestPlanetGenScreen.h" #include "TestScriptScreen.h" #include "TestStarScreen.h" #include "TestUIScreen.h" #include "TestVoxelModelScreen.h" void App::addScreens() { scrInit = new InitScreen(this); scrMainMenu = new MainMenuScreen(this, &state); scrLoad = new MainMenuLoadScreen(this, &state, scrMainMenu); scrGamePlay = new GameplayScreen(this, scrMainMenu); scrGameplayLoad = new GameplayLoadScreen(this, &state, scrMainMenu, scrGamePlay); m_screenList.addScreen(scrInit); m_screenList.addScreen(scrLoad); m_screenList.addScreen(scrMainMenu); m_screenList.addScreen(scrGameplayLoad); m_screenList.addScreen(scrGamePlay); // Add development screen scrDev = new DevScreen; scrDev->addScreen(VKEY_RETURN, scrInit, "Seed of Andromeda"); m_screenList.addScreen(scrDev); // Add test screens // scrTests.push_back(new TestConsoleScreen); // m_screenList.addScreen(scrTests.back()); // scrDev->addScreen(VKEY_C, scrTests.back(), "TestConsoleScreen"); scrTests.push_back(new TestMappingScreen); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_M, scrTests.back(), "TestMappingScreen"); scrTests.push_back(new TestDeferredScreen); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_D, scrTests.back(), "TestDeferredScreen"); scrTests.push_back(new TestBlockView); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_B, scrTests.back(), "TestBlockView"); scrTests.push_back(new TestGasGiantScreen); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_G, scrTests.back(), "TestGasGiantScreen"); scrTests.push_back(new TestScriptScreen(this, &state)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_W, scrTests.back(), "TestScriptScreen"); scrTests.push_back(new TestStarScreen(this)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_S, scrTests.back(), "TestStarScreen"); scrTests.push_back(new TestVoxelModelScreen(this)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_V, scrTests.back(), "TestVoxelModelScreen"); scrTests.push_back(new TestDisplacementMappingScreen); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_P, scrTests.back(), "TestDisplacementMappingScreen"); scrTests.push_back(new TestNoiseScreen(this)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_N, scrTests.back(), "TestNoiseScreen"); // scrTests.push_back(new TestNewBlockAPIScreen); // m_screenList.addScreen(scrTests.back()); // scrDev->addScreen(VKEY_A, scrTests.back(), "TestNewBlockAPIScreen"); scrTests.push_back(new TestPlanetGenScreen); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_O, scrTests.back(), "TestPlanetGenScreen"); scrTests.push_back(new TestConnectedTextureScreen(this, &state)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_T, scrTests.back(), "TestConnectedTextureScreen"); scrTests.push_back(new TestBiomeScreen(this, &state)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_Z, scrTests.back(), "TestBiomeScreen"); scrTests.push_back(new TestUIScreen(this, &state)); m_screenList.addScreen(scrTests.back()); scrDev->addScreen(VKEY_U, scrTests.back(), "TestUIScreen"); // Uncomment to start from dev screen for testing other screens #define START_AT_DEV_SCREEN #ifdef START_AT_DEV_SCREEN m_screenList.setScreen(scrDev->getIndex()); #else m_screenList.setScreen(scrInit->getIndex()); #endif } void App::onInit() { state.state = new SoaState; state.window = &m_window; state.soundEngine = new vsound::Engine; state.soundEngine->init(); // Load the game options SoaEngine::initOptions(soaOptions); // Set the window options soaOptions.get(OPT_FULLSCREEN).value.b = m_window.isFullscreen(); soaOptions.get(OPT_BORDERLESS).value.b = m_window.isBorderless(); soaOptions.get(OPT_SCREEN_WIDTH).value.i = m_window.getWidth(); soaOptions.get(OPT_SCREEN_HEIGHT).value.i = m_window.getHeight(); soaOptions.get(OPT_VSYNC).value.i = (m_window.getSwapInterval() == vui::GameSwapInterval::V_SYNC); // Load the options from file SoaEngine::optionsController.loadOptions(); vg::SamplerState::initPredefined(); } void App::onExit() { // Delete cache if it exists vg::SpriteBatch::disposeProgram(); } App::~App() { delete scrInit; delete scrLoad; delete scrMainMenu; delete scrGamePlay; delete scrDev; } ================================================ FILE: SoA/App.h ================================================ #pragma once #ifndef App_h_ #define App_h_ #include #include "SoaOptions.h" #include "CommonState.h" class DevScreen; class GameplayLoadScreen; class GameplayScreen; class InitScreen; class MainMenuLoadScreen; class MainMenuScreen; class TexturePackLoader; class App : public vui::MainGame { public: virtual ~App(); virtual void addScreens(); virtual void onInit(); virtual void onExit(); // Accessible Pointers To Screens InitScreen* scrInit = nullptr; MainMenuLoadScreen* scrLoad = nullptr; MainMenuScreen* scrMainMenu = nullptr; GameplayLoadScreen* scrGameplayLoad = nullptr; GameplayScreen* scrGamePlay = nullptr; DevScreen* scrDev = nullptr; std::vector scrTests; CommonState state; }; #endif // App_h_ ================================================ FILE: SoA/AtmosphereComponentRenderer.cpp ================================================ #include "stdafx.h" #include "AtmosphereComponentRenderer.h" #include "SpaceSystem.h" #include "RenderUtils.h" #include "soaUtils.h" #include "ShaderLoader.h" #include #include #include #include #include #define ICOSPHERE_SUBDIVISIONS 5 AtmosphereComponentRenderer::~AtmosphereComponentRenderer() { dispose(); } void AtmosphereComponentRenderer::initGL() { if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/AtmosphereShading/Sky.vert", "Shaders/AtmosphereShading/Sky.frag"); } if (!m_icoVbo) buildMesh(); } void AtmosphereComponentRenderer::draw(const AtmosphereComponent& aCmp, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent VORB_MAYBE_UNUSED) { m_program.use(); // Set up matrix f32m4 WVP(1.0); setMatrixScale(WVP, f32v3(aCmp.radius, aCmp.radius * (1.0 - aCmp.oblateness), aCmp.radius)); setMatrixTranslation(WVP, -relCamPos); WVP = VP * WVP; f32 camHeight = glm::length(relCamPos); f32 camHeight2 = camHeight * camHeight; // Upload uniforms glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &WVP[0][0]); glUniform3fv(m_program.getUniform("unCameraPos"), 1, &relCamPos[0]); glUniform3fv(m_program.getUniform("unLightDirWorld"), 1, &lightDir[0]); glUniform3fv(m_program.getUniform("unInvWavelength"), 1, &aCmp.invWavelength4[0]); glUniform1f(m_program.getUniform("unCameraHeight2"), camHeight2); glUniform1f(m_program.getUniform("unOuterRadius"), aCmp.radius); glUniform1f(m_program.getUniform("unOuterRadius2"), aCmp.radius * aCmp.radius); glUniform1f(m_program.getUniform("unInnerRadius"), aCmp.planetRadius); glUniform1f(m_program.getUniform("unKrESun"), aCmp.kr * aCmp.esun); glUniform1f(m_program.getUniform("unKmESun"), aCmp.km * aCmp.esun); glUniform1f(m_program.getUniform("unKr4PI"), (f32)(aCmp.kr * M_4_PI)); glUniform1f(m_program.getUniform("unKm4PI"), (f32)(aCmp.km * M_4_PI)); float scale = 1.0f / (aCmp.radius - aCmp.planetRadius); glUniform1f(m_program.getUniform("unScale"), scale); glUniform1f(m_program.getUniform("unScaleDepth"), aCmp.scaleDepth); glUniform1f(m_program.getUniform("unScaleOverScaleDepth"), scale / aCmp.scaleDepth); glUniform1i(m_program.getUniform("unNumSamples"), 3); glUniform1f(m_program.getUniform("unNumSamplesF"), 3.0f); glUniform1f(m_program.getUniform("unG"), aCmp.g); glUniform1f(m_program.getUniform("unG2"), aCmp.g * aCmp.g); // For logarithmic Z buffer glUniform1f(m_program.getUniform("unZCoef"), zCoef); // Bind VAO glBindVertexArray(m_vao); // Render glDepthMask(GL_FALSE); vg::RasterizerState::CULL_COUNTER_CLOCKWISE.set(); glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, 0); glDepthMask(GL_TRUE); vg::RasterizerState::CULL_CLOCKWISE.set(); glBindVertexArray(0); m_program.unuse(); } void AtmosphereComponentRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); if (m_icoVbo) vg::GpuMemory::freeBuffer(m_icoVbo); if (m_icoIbo) vg::GpuMemory::freeBuffer(m_icoIbo); if (m_vao) glDeleteVertexArrays(1, &m_vao); } void AtmosphereComponentRenderer::buildMesh() { std::vector indices; std::vector positions; // TODO(Ben): Optimize with LOD for far viewing vmesh::generateIcosphereMesh(ICOSPHERE_SUBDIVISIONS, indices, positions); m_numIndices = indices.size(); glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); vg::GpuMemory::createBuffer(m_icoVbo); vg::GpuMemory::createBuffer(m_icoIbo); vg::GpuMemory::bindBuffer(m_icoVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_icoVbo, vg::BufferTarget::ARRAY_BUFFER, positions.size() * sizeof(f32v3), positions.data(), vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(m_icoIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_icoIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(ui32), indices.data(), vg::BufferUsageHint::STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); m_program.enableVertexAttribArrays(); glBindVertexArray(0); } ================================================ FILE: SoA/AtmosphereComponentRenderer.h ================================================ /// /// AtmosphereComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Mar 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders atmosphere components /// #pragma once #ifndef AtmosphereComponentRenderer_h__ #define AtmosphereComponentRenderer_h__ #include #include #include #include #include struct AtmosphereComponent; struct SpaceLightComponent; class AtmosphereComponentRenderer { public: ~AtmosphereComponentRenderer(); void initGL(); void draw(const AtmosphereComponent& aCmp, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent); void dispose(); private: void buildMesh(); vg::GLProgram m_program; VGBuffer m_icoVbo = 0; VGIndexBuffer m_icoIbo = 0; VGVertexArray m_vao = 0; int m_numIndices = 0; }; #endif // AtmosphereComponentRenderer_h__ ================================================ FILE: SoA/AxisRotationComponentUpdater.cpp ================================================ #include "stdafx.h" #include "AxisRotationComponentUpdater.h" #include "SpaceSystem.h" #include "Constants.h" void AxisRotationComponentUpdater::update(SpaceSystem* spaceSystem, f64 time) { for (auto& it : spaceSystem->axisRotation) { auto& cmp = it.second; // Calculate rotation if (cmp.period) { cmp.currentRotation = (time / cmp.period) * 2.0 * M_PI; } // Calculate the axis rotation quat f64v3 eulerAngles(0, -cmp.currentRotation, 0); f64q rotationQuat = f64q(eulerAngles); // Calculate total orientation cmp.currentOrientation = cmp.axisOrientation * rotationQuat; cmp.invCurrentOrientation = glm::inverse(cmp.currentOrientation); } } ================================================ FILE: SoA/AxisRotationComponentUpdater.h ================================================ /// /// AxisRotationComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates AxisRotationComponents /// #pragma once #ifndef AxisRotationComponentUpdater_h__ #define AxisRotationComponentUpdater_h__ #include "Vorb/types.h" class SpaceSystem; class AxisRotationComponentUpdater { public: /// Updates the components /// @param spaceSystem: The ECS space system /// @param time: Time in seconds void update(SpaceSystem* spaceSystem, f64 time); }; #endif // AxisRotationComponentUpdater_h__ ================================================ FILE: SoA/Biome.cpp ================================================ #include "stdafx.h" #include "Biome.h" KEG_TYPE_DEF_SAME_NAME(BiomeFloraKegProperties, kt) { using namespace keg; KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeFloraKegProperties, id, STRING); kt.addValue("chance", Value::custom(offsetof(BiomeFloraKegProperties, chance), "NoiseBase", false)); } KEG_TYPE_DEF_SAME_NAME(BiomeTreeKegProperties, kt) { using namespace keg; KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeTreeKegProperties, id, STRING); kt.addValue("chance", Value::custom(offsetof(BiomeTreeKegProperties, chance), "NoiseBase", false)); } ================================================ FILE: SoA/Biome.h ================================================ /// /// Biome.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 30 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Provides the biome implementation. /// #pragma once #ifndef Biome_h__ #define Biome_h__ #include "Noise.h" #include "Flora.h" struct PlanetGenData; // TODO(Ben): Also support L-system trees. struct BiomeTree { NoiseBase chance; NTreeType* data = nullptr; FloraID id = FLORA_ID_NONE; }; struct BiomeFloraKegProperties { NoiseBase chance; nString id; }; KEG_TYPE_DECL(BiomeFloraKegProperties); struct BiomeTreeKegProperties { NoiseBase chance; nString id; }; KEG_TYPE_DECL(BiomeTreeKegProperties); // Unique flora instance struct BiomeFlora { NoiseBase chance; FloraType* data = nullptr; FloraID id = FLORA_ID_NONE; }; struct BlockLayer { ui32 start; ui32 width; ui16 block = 0; ui16 surfaceTransform = 0; ///< This block is used on surface }; #define BIOME_MAP_WIDTH 256 typedef nString BiomeID; struct Biome; struct BiomeInfluence { BiomeInfluence() {}; BiomeInfluence(const Biome* b, f32 weight) : b(b), weight(weight) {} const Biome* b; f32 weight; bool operator<(const BiomeInfluence& rhs) const { // Ignore weight on purpose. Only one BiomeInfluence per set or map! return b < rhs.b; } }; // TODO(Ben): Make the memory one contiguous block typedef std::vector> BiomeInfluenceMap; // TODO(Ben): Optimize the cache struct Biome { Biome():id("default"), displayName("Default"), mapColor(255, 255, 255), genData(nullptr){} BiomeID id; nString displayName; ColorRGB8 mapColor; ///< For debugging and lookups std::vector blockLayers; ///< Overrides base layers std::vector children; std::vector flora; std::vector trees; NoiseBase childNoise; ///< For sub biome determination NoiseBase terrainNoise; ///< Modifies terrain directly // Only applies to base biomes f64v2 heightRange; f64v2 heightScale; f64v2 noiseRange; f64v2 noiseScale; const PlanetGenData* genData; }; static const Biome DEFAULT_BIOME; #endif // Biome_h__ ================================================ FILE: SoA/BlendState.h ================================================ #pragma once enum BlendEquationMode { BLEND_EQUATION_MODE_FUNC_ADD = GL_FUNC_ADD, BLEND_EQUATION_MODE_FUNC_REVERSE_SUBTRACT = GL_FUNC_REVERSE_SUBTRACT, BLEND_EQUATION_MODE_FUNC_SUBTRACT = GL_FUNC_SUBTRACT, BLEND_EQUATION_MODE_MAX = GL_MAX, BLEND_EQUATION_MODE_MIN = GL_MIN }; enum BlendingFactorSrc { BLENDING_FACTOR_SRC_CONSTANT_ALPHA = GL_CONSTANT_ALPHA, BLENDING_FACTOR_SRC_CONSTANT_COLOR = GL_CONSTANT_COLOR, BLENDING_FACTOR_SRC_DST_ALPHA = GL_DST_ALPHA, BLENDING_FACTOR_SRC_DST_COLOR = GL_DST_COLOR, BLENDING_FACTOR_SRC_ONE = GL_ONE, BLENDING_FACTOR_SRC_ONE_MINUS_CONSTANT_ALPHA = GL_ONE_MINUS_CONSTANT_ALPHA, BLENDING_FACTOR_SRC_ONE_MINUS_CONSTANT_COLOR = GL_ONE_MINUS_CONSTANT_COLOR, BLENDING_FACTOR_SRC_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA, BLENDING_FACTOR_SRC_ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR, BLENDING_FACTOR_SRC_ONE_MINUS_SRC_1_ALPHA = GL_ONE_MINUS_SRC1_ALPHA, BLENDING_FACTOR_SRC_ONE_MINUS_SRC_1_COLOR = GL_ONE_MINUS_SRC1_COLOR, BLENDING_FACTOR_SRC_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA, BLENDING_FACTOR_SRC_ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR, BLENDING_FACTOR_SRC_SRC_1_ALPHA = GL_SRC1_ALPHA, BLENDING_FACTOR_SRC_SRC_1_COLOR = GL_SRC1_COLOR, BLENDING_FACTOR_SRC_SRC_ALPHA = GL_SRC_ALPHA, BLENDING_FACTOR_SRC_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE, BLENDING_FACTOR_SRC_SRC_COLOR = GL_SRC_COLOR, BLENDING_FACTOR_SRC_ZERO = GL_ZERO }; enum BlendingFactorDest { BLENDING_FACTOR_DEST_CONSTANT_ALPHA = GL_CONSTANT_ALPHA, BLENDING_FACTOR_DEST_CONSTANT_COLOR = GL_CONSTANT_COLOR, BLENDING_FACTOR_DEST_DST_ALPHA = GL_DST_ALPHA, BLENDING_FACTOR_DEST_DST_COLOR = GL_DST_COLOR, BLENDING_FACTOR_DEST_ONE = GL_ONE, BLENDING_FACTOR_DEST_ONE_MINUS_CONSTANT_ALPHA = GL_ONE_MINUS_CONSTANT_ALPHA, BLENDING_FACTOR_DEST_ONE_MINUS_CONSTANT_COLOR = GL_ONE_MINUS_CONSTANT_COLOR, BLENDING_FACTOR_DEST_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA, BLENDING_FACTOR_DEST_ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR, BLENDING_FACTOR_DEST_ONE_MINUS_SRC_1_ALPHA = GL_ONE_MINUS_SRC1_ALPHA, BLENDING_FACTOR_DEST_ONE_MINUS_SRC_1_COLOR = GL_ONE_MINUS_SRC1_COLOR, BLENDING_FACTOR_DEST_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA, BLENDING_FACTOR_DEST_ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR, BLENDING_FACTOR_DEST_SRC_1_ALPHA = GL_SRC1_ALPHA, BLENDING_FACTOR_DEST_SRC_1_COLOR = GL_SRC1_COLOR, BLENDING_FACTOR_DEST_SRC_ALPHA = GL_SRC_ALPHA, BLENDING_FACTOR_DEST_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE, BLENDING_FACTOR_DEST_SRC_COLOR = GL_SRC_COLOR, BLENDING_FACTOR_DEST_ZERO = GL_ZERO }; class BlendFunction { public: BlendFunction(BlendEquationMode bem, BlendingFactorSrc bfs, BlendingFactorDest bfd); BlendEquationMode blendMode; BlendingFactorSrc blendFactorSrc; BlendingFactorDest blendFactorDest; }; class BlendState { public: BlendState(BlendFunction funcRGB, BlendFunction funcAlpha); void set() const; BlendFunction blendFuncRGB; BlendFunction blendFuncAlpha; }; ================================================ FILE: SoA/BlockData.cpp ================================================ #include "stdafx.h" #include "BlockData.h" #include "BlockPack.h" #include "Errors.h" #include "GameManager.h" #include "SoaOptions.h" #include "ZipFile.h" KEG_ENUM_DEF(BlockOcclusion, BlockOcclusion, e) { e.addValue("none", BlockOcclusion::NONE); e.addValue("self", BlockOcclusion::SELF); // TODO(Ben): Temporary e.addValue("selfOnly", BlockOcclusion::SELF); e.addValue("all", BlockOcclusion::ALL); } KEG_TYPE_DEF_SAME_NAME(Block, kt) { kt.addValue("ID", keg::Value::basic(offsetof(Block, temp), keg::BasicType::I32)); kt.addValue("name", keg::Value::basic(offsetof(Block, name), keg::BasicType::STRING)); kt.addValue("burnTransformID", keg::Value::basic(offsetof(Block, burnTransformID), keg::BasicType::STRING)); kt.addValue("waveEffect", keg::Value::basic(offsetof(Block, waveEffect), keg::BasicType::I16)); kt.addValue("lightColor", keg::Value::basic(offsetof(Block, lightColor), keg::BasicType::UI8_V3)); kt.addValue("caPhysics", keg::Value::basic(offsetof(Block, caFilePath), keg::BasicType::STRING)); kt.addValue("waterMeshLevel", keg::Value::basic(offsetof(Block, waterMeshLevel), keg::BasicType::I16)); kt.addValue("floatingAction", keg::Value::basic(offsetof(Block, floatingAction), keg::BasicType::I16)); kt.addValue("occlusion", keg::Value::custom(offsetof(Block, occlude), "BlockOcclusion", true)); kt.addValue("spawnID", keg::Value::basic(offsetof(Block, spawnerID), keg::BasicType::STRING)); kt.addValue("sinkID", keg::Value::basic(offsetof(Block, sinkID), keg::BasicType::STRING)); kt.addValue("explosionRays", keg::Value::basic(offsetof(Block, explosionRays), keg::BasicType::UI16)); kt.addValue("meshType", keg::Value::custom(offsetof(Block, meshType), "MeshType", true)); kt.addValue("moveMod", keg::Value::basic(offsetof(Block, moveMod), keg::BasicType::F32)); kt.addValue("explosionResistance", keg::Value::basic(offsetof(Block, explosionResistance), keg::BasicType::F32)); kt.addValue("explosionPower", keg::Value::basic(offsetof(Block, explosivePower), keg::BasicType::F32)); kt.addValue("flammability", keg::Value::basic(offsetof(Block, flammability), keg::BasicType::F32)); kt.addValue("explosionPowerLoss", keg::Value::basic(offsetof(Block, explosionPowerLoss), keg::BasicType::F32)); kt.addValue("lightColorFilter", keg::Value::basic(offsetof(Block, colorFilter), keg::BasicType::F32_V3)); kt.addValue("emitter", keg::Value::basic(offsetof(Block, emitterName), keg::BasicType::STRING)); kt.addValue("movesPowder", keg::Value::basic(offsetof(Block, powderMove), keg::BasicType::BOOL)); kt.addValue("collide", keg::Value::basic(offsetof(Block, collide), keg::BasicType::BOOL)); kt.addValue("waterBreak", keg::Value::basic(offsetof(Block, waterBreak), keg::BasicType::BOOL)); kt.addValue("scatterSunRays", keg::Value::basic(offsetof(Block, blockLight), keg::BasicType::BOOL)); kt.addValue("useable", keg::Value::basic(offsetof(Block, useable), keg::BasicType::BOOL)); kt.addValue("allowsLight", keg::Value::basic(offsetof(Block, allowLight), keg::BasicType::BOOL)); kt.addValue("crushable", keg::Value::basic(offsetof(Block, isCrushable), keg::BasicType::BOOL)); kt.addValue("supportive", keg::Value::basic(offsetof(Block, isSupportive), keg::BasicType::BOOL)); } // TODO(Ben): LOL Block::Block() : lightColor(0, 0, 0), emitterName(""), emitterOnBreakName(""), emitterRandomName(""), emitter(nullptr), emitterOnBreak(nullptr), emitterRandom(nullptr) { allowLight = false; ID = 0; name = particleTexName = ""; memset(textures, 0, sizeof(textures)); particleTex = 0; collide = true; occlude = BlockOcclusion::ALL; meshType = MeshType::BLOCK; waveEffect = 0; explosionResistance = 1.0; active = 0; useable = 1; blockLight = true; waterMeshLevel = 0; waterBreak = false; isCrushable = false; floatingAction = 1; flammability = 0.0f; isSupportive = true; explosivePower = 0.0; explosionPowerLoss = 0.0; explosionRays = 0; powderMove = true; moveMod = 1.0f; spawnerID = ""; sinkID = ""; colorFilter = f32v3(1.0f); } ================================================ FILE: SoA/BlockData.h ================================================ #pragma once #include "stdafx.h" #include #include #include #include #include "CAEngine.h" #include "ChunkMesh.h" #include "BlockTexture.h" #include "Constants.h" #include "Item.h" #define GETFLAGS(a) ((a) >> 12) #define GETFLAG1(a) (((a) & 0x8000) >> 15) #define GETFLAG2(a) (((a) & 0x4000) >> 14) #define GETFLAG3(a) (((a) & 0x2000) >> 13) #define GETFLAG4(a) (((a) & 0x1000) >> 12) #define GETBLOCKID(a) (((a) & 0x0FFF)) #define SETFLAGS(a, b) ((a) = ((a) | ((b) << 12))) enum class BlockOcclusion { NONE, ALL, SELF, SELF_ONLY }; KEG_ENUM_DECL(BlockOcclusion); class BlockTextureFaces { public: union { ui32 array[6]; /// Access 6-sided block textures as an array class { public: ui32 nx; /// Negative x-axis texture ui32 px; /// Positive x-axis texture ui32 ny; /// Negative y-axis texture ui32 py; /// Positive y-axis texture ui32 nz; /// Negative z-axis texture ui32 pz; /// Positive z-axis texture }; /// Textures named in cardinal convention }; ui32& operator[] (const i32& i) { return array[i]; } const ui32& operator[] (const i32& i) const { return array[i]; } }; typedef nString BlockIdentifier; ///< Unique identifier key for blocks typedef ui16 BlockID; class Block { public: Block(); void SetAvgTexColors(); i32 temp; BlockIdentifier sID; nString name; BlockID ID; nString burnTransformID; i16 waveEffect; ui16 lightColorPacked; /// 5 bit RGB light color packed into a ui16 i16 waterMeshLevel; i16 floatingAction; nString spawnerID; nString sinkID; ui16 explosionRays; ui16 floraHeight = 0; ui16 liquidStartID = 0; ui16 liquidLevels = 0; BlockOcclusion occlude; MeshType meshType; GLfloat moveMod; GLfloat explosionResistance; GLfloat explosivePower; GLfloat flammability; GLfloat explosionPowerLoss; f32v3 colorFilter; int caIndex = -1; CAAlgorithm caAlg = CAAlgorithm::NONE; nString caFilePath = ""; ColorRGB8 lightColor; ui8 particleTex; bool powderMove; bool collide; bool waterBreak; bool blockLight; bool useable; bool allowLight; bool isCrushable; bool isSupportive; bool active; union { struct { BlockTexture* textureLeft; BlockTexture* textureRight; BlockTexture* textureBottom; BlockTexture* textureTop; BlockTexture* textureBack; BlockTexture* textureFront; }; BlockTexture* textures[6]; }; // TODO(Ben): NOPE // ... but why? nString particleTexName; nString emitterName, emitterOnBreakName, emitterRandomName; class ParticleEmitter *emitter, *emitterOnBreak, *emitterRandom; std::vector altColors; }; KEG_TYPE_DECL(Block); ================================================ FILE: SoA/BlockLoader.cpp ================================================ #include "stdafx.h" #include "BlockLoader.h" #include #include #include "BlockPack.h" #include "Chunk.h" #include "Errors.h" #include "GameManager.h" #include "VoxelBits.h" #define BLOCK_MAPPING_PATH "BlockMapping.ini" #define BLOCK_DATA_PATH "BlockData.yml" #define BINARY_CACHE_PATH "BlockCache.bin" bool BlockLoader::loadBlocks(const vio::IOManager& iom, BlockPack* pack) { // Load existing mapping if there is one tryLoadMapping(iom, BLOCK_MAPPING_PATH, pack); // Check for binary cache vio::Path binPath; bool useCache = false; if (iom.resolvePath(BINARY_CACHE_PATH, binPath)) { vio::Path dataPath; if (!iom.resolvePath(BLOCK_DATA_PATH, dataPath)) return false; if (binPath.getLastModTime() >= dataPath.getLastModTime()) { useCache = true; } } // Clear CA physics cache CaPhysicsType::clearTypes(); GameBlockPostProcess bpp(&iom, &CaPhysicsType::typesCache); pack->onBlockAddition += bpp.del; if (useCache) { if (!BlockLoader::loadBinary(iom, BINARY_CACHE_PATH, pack)) { printf("Failed to load binary cache %s\n", BINARY_CACHE_PATH); if (!BlockLoader::load(iom, BLOCK_DATA_PATH, pack)) { pack->onBlockAddition -= bpp.del; return false; } } } else { if (!BlockLoader::load(iom, BLOCK_DATA_PATH, pack)) { pack->onBlockAddition -= bpp.del; return false; } } pack->onBlockAddition -= bpp.del; saveMapping(iom, BLOCK_MAPPING_PATH, pack); if (!useCache) { // saveBinary(iom, BINARY_CACHE_PATH, pack); } return true; } // Conditional keg write #define COND_WRITE_KEG(key, var) if (b.var != d.var) { writer.push(keg::WriterParam::KEY) << nString(key); writer.push(keg::WriterParam::VALUE) << b.var; } bool BlockLoader::saveBlocks(const nString& filePath, BlockPack* pack) { // Open the portal to Hell std::ofstream file(filePath); if (file.fail()) return false; BlockPack& blocks = *pack; std::map sortMap; const std::vector& blockList = blocks.getBlockList(); for (size_t i = 0; i < blockList.size(); i++) { const Block& b = blockList[i]; if (b.active) { sortMap[b.sID] = &b; } } // Default block Block d; // Emit data keg::YAMLWriter writer; writer.push(keg::WriterParam::BEGIN_MAP); for (auto& it : sortMap) { const Block& b = *it.second; // Write the block name first writer.push(keg::WriterParam::KEY) << b.sID; // Write the block data now writer.push(keg::WriterParam::VALUE); writer.push(keg::WriterParam::BEGIN_MAP); COND_WRITE_KEG("allowsLight", allowLight); COND_WRITE_KEG("collide", collide); COND_WRITE_KEG("crushable", isCrushable); COND_WRITE_KEG("explosionPower", explosivePower); COND_WRITE_KEG("explosionPowerLoss", explosionPowerLoss); COND_WRITE_KEG("explosionRays", explosionRays); COND_WRITE_KEG("explosionResistance", explosionResistance); COND_WRITE_KEG("flammability", flammability); COND_WRITE_KEG("floatingAction", floatingAction); if (b.colorFilter != d.colorFilter) { writer.push(keg::WriterParam::KEY) << nString("lightColorFilter"); writer.push(keg::WriterParam::VALUE) << keg::kegf32v3(b.colorFilter); } if (b.meshType != d.meshType) { writer.push(keg::WriterParam::KEY) << nString("meshType"); switch (b.meshType) { case MeshType::NONE: writer.push(keg::WriterParam::VALUE) << nString("none"); break; case MeshType::BLOCK: writer.push(keg::WriterParam::VALUE) << nString("cube"); break; case MeshType::LEAVES: writer.push(keg::WriterParam::VALUE) << nString("leaves"); break; case MeshType::TRIANGLE: writer.push(keg::WriterParam::VALUE) << nString("triangle"); break; case MeshType::CROSSFLORA: writer.push(keg::WriterParam::VALUE) << nString("cross"); break; case MeshType::LIQUID: writer.push(keg::WriterParam::VALUE) << nString("liquid"); break; case MeshType::FLAT: writer.push(keg::WriterParam::VALUE) << nString("flat"); break; } } COND_WRITE_KEG("moveMod", moveMod); COND_WRITE_KEG("name", name); switch (b.occlude) { case BlockOcclusion::NONE: writer.push(keg::WriterParam::KEY) << nString("occlusion"); writer.push(keg::WriterParam::VALUE) << nString("none"); break; case BlockOcclusion::ALL: writer.push(keg::WriterParam::KEY) << nString("occlusion"); writer.push(keg::WriterParam::VALUE) << nString("all"); break; case BlockOcclusion::SELF: writer.push(keg::WriterParam::KEY) << nString("occlusion"); writer.push(keg::WriterParam::VALUE) << nString("self"); break; case BlockOcclusion::SELF_ONLY: writer.push(keg::WriterParam::KEY) << nString("occlusion"); writer.push(keg::WriterParam::VALUE) << nString("selfOnly"); break; } COND_WRITE_KEG("sinkID", sinkID); COND_WRITE_KEG("spawnerID", spawnerID); COND_WRITE_KEG("supportive", isSupportive); COND_WRITE_KEG("waterBreak", waterBreak); //keg::write((ui8*)b, writer, keg::getGlobalEnvironment(), &KEG_GLOBAL_TYPE(Block)); writer.push(keg::WriterParam::END_MAP); } writer.push(keg::WriterParam::END_MAP); file << writer.c_str(); file.flush(); file.close(); return true; } bool BlockLoader::load(const vio::IOManager& iom, const cString filePath, BlockPack* pack) { // Read file nString data; iom.readFileToString(filePath, data); if (data.empty()) return false; // Convert to YAML keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { context.reader.dispose(); return false; } // Load all block nodes std::vector loadedBlocks; auto f = makeFunctor([&] (Sender, const nString& key, keg::Node value) { // Add a block loadedBlocks.emplace_back(); Block& b = loadedBlocks.back(); // Set sID to key b.sID = key; // Load data keg::parse((ui8*)&b, value, context, &KEG_GLOBAL_TYPE(Block)); }); context.reader.forAllInMap(node, &f); context.reader.dispose(); // Add blocks to pack for (auto& b : loadedBlocks) { pack->append(b); } return true; } GameBlockPostProcess::GameBlockPostProcess(const vio::IOManager* iom, CaPhysicsTypeDict* caCache) : m_iom(iom), m_caCache(caCache) { del = makeDelegate(this, &GameBlockPostProcess::invoke); } void GameBlockPostProcess::invoke(Sender s, ui16 id) { Block& block = ((BlockPack*)s)->operator[](id); block.active = true; // Pack light color block.lightColorPacked = ((ui16)block.lightColor.r << LAMP_RED_SHIFT) | ((ui16)block.lightColor.g << LAMP_GREEN_SHIFT) | (ui16)block.lightColor.b; // Ca Physics if (block.caFilePath.length()) { // Check if this physics type was already loaded auto it = m_caCache->find(block.caFilePath); if (it == m_caCache->end()) { // TODO(Ben): Why new? // TODO(Ben): This isn't getting stored... CaPhysicsType* newType = new CaPhysicsType(); // Load in the data if (newType->loadFromYml(block.caFilePath, m_iom)) { block.caIndex = newType->getCaIndex(); block.caAlg = newType->getCaAlg(); } else { delete newType; } } else { block.caIndex = it->second->getCaIndex(); block.caAlg = it->second->getCaAlg(); } } } bool BlockLoader::saveMapping(const vio::IOManager& iom, const cString filePath, BlockPack* pack) { vio::FileStream fs = iom.openFile(filePath, vio::FileOpenFlags::WRITE_ONLY_CREATE); if (!fs.isOpened()) pError("Failed to open block mapping file for save"); for (auto& b : pack->getBlockMap()) { fs.write("%s: %d\n", b.first.c_str(), b.second); } return true; } bool BlockLoader::tryLoadMapping(const vio::IOManager& iom, const cString filePath, BlockPack* pack) { vio::Path path; if (!iom.resolvePath(filePath, path)) return false; std::ifstream file(path.getCString()); if (file.fail()) return false; // TODO(Ben): Handle comments nString token; BlockID id; while (std::getline(file, token, ':')) { // Read the id file >> id; pack->reserveID(token, id); // Get the newline char nl; file.get(nl); } return true; } bool BlockLoader::saveBinary(const vio::IOManager& iom, const cString filePath, BlockPack* pack) { vio::FileStream fs = iom.openFile(filePath, vio::FileOpenFlags::WRITE_ONLY_CREATE | vio::FileOpenFlags::BINARY); if (!fs.isOpened()) return false; ui32 size = pack->getBlockMap().size(); ui32 blockSize = sizeof(Block); // Write sizes fs.write(1, sizeof(ui32), &size); fs.write(1, sizeof(ui32), &blockSize); // TODO(Ben): This isn't complete. const std::vector& blockList = pack->getBlockList(); for (auto& b : pack->getBlockMap()) { const Block& block = blockList[b.second]; fs.write("%s", block.name.c_str()); fs.write(1, 1, "\0"); fs.write(1, sizeof(bool), &block.powderMove); fs.write(1, sizeof(bool), &block.collide); fs.write(1, sizeof(bool), &block.waterBreak); fs.write(1, sizeof(bool), &block.blockLight); fs.write(1, sizeof(bool), &block.useable); fs.write(1, sizeof(bool), &block.allowLight); fs.write(1, sizeof(bool), &block.isCrushable); fs.write(1, sizeof(bool), &block.isSupportive); } return true; } void readStr(vio::FileStream& fs, char* buf) { int i = 0; do { fs.read(1, sizeof(char), buf + i); } while (buf[i++] != 0); } bool BlockLoader::loadBinary(const vio::IOManager& iom, const cString filePath, BlockPack* pack) { vio::FileStream fs = iom.openFile(filePath, vio::FileOpenFlags::READ_ONLY_EXISTING | vio::FileOpenFlags::BINARY); if (!fs.isOpened()) return false; ui32 size; ui32 blockSize; // Read sizes fs.read(1, sizeof(ui32), &size); fs.read(1, sizeof(ui32), &blockSize); // Make sure block size didn't change. DEBUG MODE CHANGES THE SIZE!!! //if (blockSize != sizeof(Block)) return false; char buf[512]; // TODO(Ben): This isn't complete. for (ui32 i = 0; i < size; i++) { Block b; readStr(fs, buf); b.name = buf; fs.read(1, sizeof(bool), &b.powderMove); fs.read(1, sizeof(bool), &b.collide); fs.read(1, sizeof(bool), &b.waterBreak); fs.read(1, sizeof(bool), &b.blockLight); fs.read(1, sizeof(bool), &b.useable); fs.read(1, sizeof(bool), &b.allowLight); fs.read(1, sizeof(bool), &b.isCrushable); fs.read(1, sizeof(bool), &b.isSupportive); pack->append(b); } return true; } ================================================ FILE: SoA/BlockLoader.h ================================================ #pragma once #include #include #include "BlockData.h" #include "CAEngine.h" DECL_VIO(class IOManager) class BlockPack; class TexturePackLoader; class GameBlockPostProcess { public: GameBlockPostProcess(const vio::IOManager* iom, CaPhysicsTypeDict* caCache); void invoke(Sender s, ui16 id); Delegate del; private: const vio::IOManager* m_iom; ///< IO workspace CaPhysicsTypeDict* m_caCache; ///< CA type cache }; class BlockLoader { public: /// Loads blocks from a .yml file /// @return true on success, false on failure static bool loadBlocks(const vio::IOManager& iom, BlockPack* pack); /// Loads blocks from a .yml file /// @param iom: IO workspace /// @param filePath: The .yml file path /// @param pack: Depository for all loaded blocks /// @return true on success, false on failure static bool load(const vio::IOManager& iom, const cString filePath, BlockPack* pack); /// Saves blocks to a .yml file /// @param filePath: The .yml file path /// @param pack: Source of block data /// @return true on success, false on failure static bool saveBlocks(const nString& filePath, BlockPack* pack); private: /// Sets up the water blocks. This is temporary /// @param blocks: Output list for blocks static void SetWaterBlocks(std::vector& blocks); /// Saves the block mapping scheme static bool saveMapping(const vio::IOManager& iom, const cString filePath, BlockPack* pack); /// Tries to load an existing block mapping scheme static bool tryLoadMapping(const vio::IOManager& iom, const cString filePath, BlockPack* pack); /// Caches blocks in binary static bool saveBinary(const vio::IOManager& iom, const cString filePath, BlockPack* pack); /// Tries to load an existing block mapping scheme static bool loadBinary(const vio::IOManager& iom, const cString filePath, BlockPack* pack); }; ================================================ FILE: SoA/BlockPack.cpp ================================================ #include "stdafx.h" #include "BlockPack.h" BlockPack::BlockPack() : onBlockAddition(this) { { // Create "None" block Block b; b.sID = "none"; b.name = "None"; b.allowLight = true; b.collide = false; b.floatingAction = 0; b.meshType = MeshType::NONE; b.occlude = BlockOcclusion::NONE; b.isSupportive = false; b.blockLight = false; b.useable = true; append(b); } { // Create "Unknown" block Block b; b.sID = "Unknown"; b.name = "Unknown"; append(b); } } BlockID BlockPack::append(Block& block) { const Block* curBlock; BlockID rv; if ((curBlock = hasBlock(block.sID))) { rv = curBlock->ID; block.ID = rv; // Overwrite block *const_cast(curBlock) = block; } else { rv = m_blockList.size(); block.ID = rv; // Add a new block m_blockList.push_back(block); // Set the correct index m_blockMap[block.sID] = rv; } onBlockAddition(block.ID); return rv; } void BlockPack::reserveID(const BlockIdentifier& sid, const BlockID& id) { if (id >= m_blockList.size()) m_blockList.resize(id + 1); m_blockMap[sid] = id; m_blockList[id].ID = id; } ================================================ FILE: SoA/BlockPack.h ================================================ /// /// BlockPack.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 23 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Container for block data /// #pragma once #ifndef BlockPack_h__ #define BlockPack_h__ #include #include #include "BlockData.h" /// A container for blocks class BlockPack { public: /// Constructor which adds default none and unknown blocks BlockPack(); /// Add a block to the pack, and overwrite a block of the same BlockIdentifier /// Will invalidate existing Block* pointers. Store BlockIDs instead. BlockID append(Block& block); void reserveID(const BlockIdentifier& sid, const BlockID& id); /// Note that the returned pointer becomes invalidated after an append call /// @return nullptr if block doesn't exist const Block* hasBlock(const BlockID& id) const { if (id >= m_blockList.size()) { return nullptr; } else { return &m_blockList[id]; } } const Block* hasBlock(const BlockIdentifier& sid) const { auto v = m_blockMap.find(sid); if (v == m_blockMap.end()) { return nullptr; } else { return &m_blockList[v->second]; } } /// @return Number of blocks in this pack size_t size() const { return m_blockList.size(); } /************************************************************************/ /* Block accessors */ /************************************************************************/ Block& operator[](const size_t& index) { return m_blockList[index]; } const Block& operator[](const size_t& index) const { return m_blockList[index]; } Block& operator[](const BlockIdentifier& sid) { return m_blockList[m_blockMap.at(sid)]; } const Block& operator[](const BlockIdentifier& sid) const { return m_blockList[m_blockMap.at(sid)]; } const ui16& getBlockIndex(const BlockIdentifier& sid) const { return m_blockMap.at(sid); } const std::unordered_map& getBlockMap() const { return m_blockMap; } const std::vector& getBlockList() const { return m_blockList; } Event onBlockAddition; ///< Signaled when a block is loaded private: std::unordered_map m_blockMap; ///< Blocks indices organized by identifiers std::vector m_blockList; ///< Block data list }; #endif // BlockPack_h__ ================================================ FILE: SoA/BlockTexture.cpp ================================================ #include "stdafx.h" #include "BlockTexture.h" #include "BlockTexturePack.h" KEG_ENUM_DEF(ConnectedTextureReducedMethod, ConnectedTextureReducedMethod, e) { e.addValue("none", ConnectedTextureReducedMethod::NONE); e.addValue("top", ConnectedTextureReducedMethod::TOP); e.addValue("bottom", ConnectedTextureReducedMethod::BOTTOM); } KEG_ENUM_DEF(BlendType, BlendType, e) { e.addValue("add", BlendType::ADD); e.addValue("multiply", BlendType::MULTIPLY); e.addValue("replace", BlendType::ALPHA); e.addValue("subtract", BlendType::SUBTRACT); } KEG_ENUM_DEF(ConnectedTextureMethods, ConnectedTextureMethods, e) { e.addValue("none", ConnectedTextureMethods::NONE); e.addValue("connect", ConnectedTextureMethods::CONNECTED); e.addValue("random", ConnectedTextureMethods::RANDOM); e.addValue("repeat", ConnectedTextureMethods::REPEAT); e.addValue("grass", ConnectedTextureMethods::GRASS); e.addValue("horizontal", ConnectedTextureMethods::HORIZONTAL); e.addValue("vertical", ConnectedTextureMethods::VERTICAL); e.addValue("flora", ConnectedTextureMethods::FLORA); } KEG_ENUM_DEF(ConnectedTextureSymmetry, ConnectedTextureSymmetry, e) { e.addValue("none", ConnectedTextureSymmetry::NONE); e.addValue("opposite", ConnectedTextureSymmetry::OPPOSITE); e.addValue("all", ConnectedTextureSymmetry::ALL); } KEG_TYPE_DEF_SAME_NAME(BlockTexture, kt) { kt.addValue("base", keg::Value::custom(offsetOf(BlockTexture, layers.base), "BlockTextureLayer")); kt.addValue("overlay", keg::Value::custom(offsetOf(BlockTexture, layers.overlay), "BlockTextureLayer")); kt.addValue("blendMode", keg::Value::custom(offsetOf(BlockTexture, blendMode), "BlendType", true)); } /// "less than" operator for inserting into sets in TexturePackLoader bool BlockTextureLayer::operator<(const BlockTextureLayer& b) const { // Helper macro for checking if != #define LCHECK(a) if (a < b.a) { return true; } else if (a > b.a) { return false; } LCHECK(path); LCHECK(method); LCHECK(size.x); LCHECK(size.y); LCHECK(symmetry); LCHECK(color.r); LCHECK(color.g); LCHECK(color.b); LCHECK(reducedMethod); LCHECK(weights.size()); LCHECK(totalWeight); LCHECK(numTiles); LCHECK(innerSeams); LCHECK(transparency); return false; } void BlockTextureLayer::getFinalColor(OUT color3& resColor, ui8 temperature, ui8 rainfall, ui32 altColor VORB_UNUSED) const { // TODO(Ben): Alternate colors if (colorMap) { // TODO(Ben): Store as floats to prevent cast overhead? const color3& mapColor = colorMap->pixels[rainfall][temperature]; //Average the map color with the base color resColor.r = (ui8)(((f32)color.r * (f32)mapColor.r) / 255.0f); resColor.g = (ui8)(((f32)color.g * (f32)mapColor.g) / 255.0f); resColor.b = (ui8)(((f32)color.b * (f32)mapColor.b) / 255.0f); } /*else if (altColor > altColors.size()) { //alt colors, for leaves and such baseColor = altColors[flags - 1]; } */else { resColor = color; } } ================================================ FILE: SoA/BlockTexture.h ================================================ /// /// BlockTexture.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 16 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Texture information for blocks /// #pragma once #ifndef BlockTexture_h__ #define BlockTexture_h__ #include #include "BlockTextureMethods.h" #define BASE_TYPE_INDEX 0 #define NORM_TYPE_INDEX 1 #define DISP_TYPE_INDEX 2 struct BlockColorMap; enum class ConnectedTextureMethods { NONE, CONNECTED, HORIZONTAL, VERTICAL, GRASS, REPEAT, RANDOM, FLORA }; KEG_ENUM_DECL(ConnectedTextureMethods); enum class ConnectedTextureSymmetry { NONE, OPPOSITE, ALL }; KEG_ENUM_DECL(ConnectedTextureSymmetry); enum class ConnectedTextureReducedMethod { NONE, TOP, BOTTOM }; KEG_ENUM_DECL(ConnectedTextureReducedMethod); enum class BlendType { ALPHA, ADD, SUBTRACT, MULTIPLY }; KEG_ENUM_DECL(BlendType); class BlockTextureLayer { public: BlockTextureLayer() : method(ConnectedTextureMethods::NONE), size(1), symmetry(ConnectedTextureSymmetry::NONE), reducedMethod(ConnectedTextureReducedMethod::NONE), colorMap(nullptr), averageColor(255, 255, 255), color(255, 255, 255), floraHeight(0), totalWeight(0), numTiles(1), indices{0, 0, 0}, innerSeams(false), transparency(false), path(""), normalPath(""), dispPath(""), colorMapPath(""), blockTextureFunc(BlockTextureMethods::getDefaultTextureIndex) {} static ui32 getFloraRows(ui32 floraMaxHeight) { return (floraMaxHeight * floraMaxHeight + floraMaxHeight) / 2; } // Sets the texture func based on the method // needs to have the method void initBlockTextureFunc() { switch (method) { case ConnectedTextureMethods::CONNECTED: blockTextureFunc = BlockTextureMethods::getConnectedTextureIndex; break; case ConnectedTextureMethods::RANDOM: blockTextureFunc = BlockTextureMethods::getRandomTextureIndex; break; case ConnectedTextureMethods::GRASS: blockTextureFunc = BlockTextureMethods::getGrassTextureIndex; break; case ConnectedTextureMethods::HORIZONTAL: blockTextureFunc = BlockTextureMethods::getHorizontalTextureIndex; break; case ConnectedTextureMethods::VERTICAL: blockTextureFunc = BlockTextureMethods::getVerticalTextureIndex; break; case ConnectedTextureMethods::FLORA: blockTextureFunc = BlockTextureMethods::getFloraTextureIndex; break; default: break; } } // TODO(Ben): should it be ref color? inline void getBlockTextureMethodData(BlockTextureMethodParams& params, OUT color3& color, OUT BlockTextureMethodData& data) const { data.index = this->index.layer; getTextureMethodData(params, BASE_TYPE_INDEX, color, data); } inline void getNormalTextureMethodData(BlockTextureMethodParams& params, OUT color3& color, OUT BlockTextureMethodData& data) const { data.index = this->index.normal; return getTextureMethodData(params, NORM_TYPE_INDEX, color, data); } inline void getDispTextureMethodData(BlockTextureMethodParams& params, OUT color3& color, OUT BlockTextureMethodData& data) const { data.index = this->index.disp; return getTextureMethodData(params, DISP_TYPE_INDEX, color, data); } inline void getTextureMethodData(BlockTextureMethodParams& params, ui32 typeIndex, OUT color3& color, BlockTextureMethodData& data) const { params.set(this, typeIndex, color); blockTextureFunc(params, data); } void getFinalColor(OUT color3& color, ui8 temperature, ui8 rainfall, ui32 altColor) const; ConnectedTextureMethods method; ui8v2 size; ConnectedTextureSymmetry symmetry; ConnectedTextureReducedMethod reducedMethod; BlockColorMap* colorMap; color3 averageColor; // Average texture color combined with color (for terrain) color3 color; ui32 floraHeight; Array weights; ui32 totalWeight; ui32 numTiles; union { struct { BlockTextureIndex layer; BlockTextureIndex normal; BlockTextureIndex disp; } index; BlockTextureIndex indices[3]; }; bool innerSeams; bool transparency; nString path; nString normalPath; nString dispPath; nString colorMapPath; BlockTextureFunc blockTextureFunc; /// "less than" operator for inserting into sets in TexturePackLoader // TODO(Ben): Are these operators needed? bool operator<(const BlockTextureLayer& b) const; bool operator==(const BlockTextureLayer& b) const { return method == b.method && size == b.size && symmetry == b.symmetry && reducedMethod == b.reducedMethod && colorMap == b.colorMap && color == b.color && averageColor == b.averageColor && floraHeight == b.floraHeight && totalWeight == b.totalWeight && numTiles == b.numTiles && index.layer == b.index.layer && innerSeams == b.innerSeams && transparency == b.transparency && path == b.path; } }; struct BlockTexture { BlockTexture(): layers(), blendMode(BlendType::ALPHA) { } //provide deconstructor because of union ~BlockTexture() { layers.base.BlockTextureLayer::~BlockTextureLayer(); layers.overlay.BlockTextureLayer::~BlockTextureLayer(); } struct { BlockTextureLayer base; BlockTextureLayer overlay; } layers; BlendType blendMode; }; KEG_TYPE_DECL(BlockTexture); #endif // BlockTexture_h__ ================================================ FILE: SoA/BlockTextureAtlas.h ================================================ #pragma once // Number Of Tiles Per Side Of The Block Texture Atlas const i32 BLOCK_TEXTURE_ATLAS_TILES_PER_SIDE = 16; const i32 BLOCK_TEXTURE_ATLAS_TILES_PER_PAGE = BLOCK_TEXTURE_ATLAS_TILES_PER_SIDE * BLOCK_TEXTURE_ATLAS_TILES_PER_SIDE; class BlockTextureIndex { public: BlockTextureIndex(ui16 blockID, i32 tileWidth, i32 tileHeight); BlockTextureIndex() : BlockTextureIndex(0, 0, 0) {} void setIndex(const i32& x, const i32& y) { atlasUVRect[0] = (ui8)((x << 4) | y); } void setPage(const i32& page) { atlasUVRect[1] = (ui8)page; } void setSize(const i32& x, const i32& y) { atlasUVRect[2] = (ui8)((x << 4) | y); } // The Location And Size In The Atlas ui8 atlasUVRect[3]; // Texturing Method ui8 textureMethod; }; class BlockAtlasPage; class BlockTextureAtlas { public: BlockTextureAtlas(i32 tileResolution); void addTextures(std::vector& textures); private: i32v3 _atlasDimensions; i32 _resolution; std::vector _atlasPages; std::vector _sortedOrderTiles; }; ================================================ FILE: SoA/BlockTextureLoader.cpp ================================================ #include "stdafx.h" #include "BlockTextureLoader.h" #include "ModPathResolver.h" #include "BlockTexturePack.h" #include "BlockData.h" #include "Errors.h" #include // Used for error checking #define CONNECTED_WIDTH 12 #define CONNECTED_HEIGHT 4 #define GRASS_WIDTH 3 #define GRASS_HEIGHT 3 #define HORIZONTAL_WIDTH 4 #define HORIZONTAL_HEIGHT 1 void BlockTextureLoader::init(ModPathResolver* texturePathResolver, BlockTexturePack* texturePack) { m_texturePathResolver = texturePathResolver; m_texturePack = texturePack; } void BlockTextureLoader::loadTextureData() { if (!loadLayerProperties()) pError("Failed to load LayerProperties.yml"); if (!loadTextureProperties()) pError("Failed to load Textures.yml"); if (!loadBlockTextureMapping()) pError("Failed to load BlockTextureMapping.yml"); } void BlockTextureLoader::loadBlockTextures(Block& block) { // Check for block mapping auto it = m_blockMappings.find(block.sID); if (it == m_blockMappings.end()) { printf("Warning: Could not load texture mapping for block %s\n", block.sID.c_str()); for (int i = 0; i < 6; i++) { block.textures[i] = m_texturePack->getDefaultTexture(); } return; } // Load the textures for each face BlockTextureNames& names = it->second; for (int i = 0; i < 6; i++) { BlockTexture* texture = m_texturePack->findTexture(names.names[i]); if (texture) { loadLayer(texture->layers.base); if (texture->layers.overlay.path.size()) { loadLayer(texture->layers.overlay); } block.textures[i] = texture; } else { block.textures[i] = m_texturePack->getDefaultTexture(); return; } } // TODO(Ben): NOPE /* BlockTexture particleTexture; GameManager::texturePackLoader->getBlockTexture(particleTexName, particleTexture); particleTex = particleTexture.base.index;*/ // Calculate flora height // TODO(Ben): This is dubious if (block.textures[0]->layers.base.method == ConnectedTextureMethods::FLORA) { // Just a bit of algebra to solve for n with the equation y = (n^2 + n) / 2 // which becomes n = (sqrt(8 * y + 1) - 1) / 2 int y = block.textures[0]->layers.base.size.y; block.floraHeight = (ui16)(sqrt(8 * y + 1) - 1) / 2; } } bool BlockTextureLoader::loadLayerProperties() { vio::Path path; if (!m_texturePathResolver->resolvePath("LayerProperties.yml", path)) return false; // Read file nString data; m_iom.readFileToString(path, data); if (data.empty()) return false; // Convert to YAML keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { context.reader.dispose(); return false; } // Layer handle for lf BlockTextureLayer* lp; // Custom values for parsing keg::Value colorVal = keg::Value::basic(0, keg::BasicType::UI8_V3); keg::Value methodVal = keg::Value::custom(0, "ConnectedTextureMethods", true); // Custom layer parse auto lf = makeFunctor([&, this](Sender, const nString& key, keg::Node value) { if (key == "path") { lp->path = keg::convert(value); } else if (key == "normalMap") { lp->normalPath = keg::convert(value); } else if (key == "dispMap") { lp->dispPath = keg::convert(value); } else if (key == "color") { switch (keg::getType(value)) { case keg::NodeType::VALUE: lp->colorMapPath = keg::convert(value); lp->colorMap = this->getTexturePack()->getColorMap(lp->colorMapPath); break; case keg::NodeType::SEQUENCE: keg::evalData((ui8*)&lp->color, &colorVal, value, context); break; default: break; } } else if (key == "altColors") { // TODO(Ben): Implement } else if (key == "method") { keg::evalData((ui8*)&lp->method, &methodVal, value, context); } else if (key == "coupling") { // TODO(Ben): Implement } }); // Load all layers auto f = makeFunctor([&](Sender, const nString& key, keg::Node value) { BlockTextureLayer layer; lp = &layer; // Manual parse context.reader.forAllInMap(value, &lf); // Cache the layer m_layers[key] = layer; }); context.reader.forAllInMap(node, &f); context.reader.dispose(); return true; } // For parsing a block texture #define TEXTURE_PARSE_CODE \ if (key == "base") { \ if (keg::getType(value) == keg::NodeType::MAP) { \ } else { \ nString base = keg::convert(value); \ auto& it = m_layers.find(base); \ if (it != m_layers.end()) { \ texture->base = it->second; \ } \ } \ } else if (key == "overlay") { \ if (keg::getType(value) == keg::NodeType::MAP) { \ } else { \ nString overlay = keg::convert(value); \ auto& it = m_layers.find(overlay); \ if (it != m_layers.end()) { \ texture->overlay = it->second; \ } \ } \ } else if (key == "blendMode") { \ nString v = keg::convert(value); \ if (v == "add") { \ texture->blendMode = BlendType::ADD; \ } else if (v == "multiply") { \ texture->blendMode = BlendType::MULTIPLY; \ } else if (v == "subtract") { \ texture->blendMode = BlendType::SUBTRACT; \ } \ } \ bool BlockTextureLoader::loadTextureProperties() { vio::Path path; if (!m_texturePathResolver->resolvePath("Textures.yml", path)) return false; // Read file nString data; m_iom.readFileToString(path, data); if (data.empty()) return false; // Convert to YAML keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { context.reader.dispose(); return false; } BlockTexture* texture; auto valf = makeFunctor([&](Sender, const nString& key, keg::Node value) { // TEXTURE_PARSE_CODE; if(key=="base") { if(keg::getType(value)==keg::NodeType::MAP) { } else { nString base=keg::convert(value); auto it=m_layers.find(base); if(it!=m_layers.end()) { texture->layers.base = it->second; } } } else if(key=="overlay") { if(keg::getType(value)==keg::NodeType::MAP) { } else { nString overlay=keg::convert(value); auto it=m_layers.find(overlay); if(it!=m_layers.end()) { texture->layers.overlay = it->second; } } } else if(key=="blendMode") { nString v=keg::convert(value); if(v=="add") { texture->blendMode=BlendType::ADD; } else if(v=="multiply") { texture->blendMode=BlendType::MULTIPLY; } else if(v=="subtract") { texture->blendMode=BlendType::SUBTRACT; } } }); // Load all layers auto f = makeFunctor([&](Sender, const nString& key, keg::Node value) { texture = m_texturePack->getNextFreeTexture(key); context.reader.forAllInMap(value, &valf); }); context.reader.forAllInMap(node, &f); context.reader.dispose(); return true; } bool BlockTextureLoader::loadBlockTextureMapping() { vio::Path path; if (!m_texturePathResolver->resolvePath("BlockTextureMapping.yml", path)) return false; // Read file nString data; const nString* blockName; BlockTexture* texture; m_iom.readFileToString(path, data); if (data.empty()) return false; // Convert to YAML keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { context.reader.dispose(); return false; } // For parsing textures auto texParseFunctor = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "base") { if (keg::getType(value) == keg::NodeType::MAP) { } else { nString base = keg::convert(value); auto it = m_layers.find(base); if (it != m_layers.end()) { texture->layers.base = it->second; } } } else if (key == "overlay") { if (keg::getType(value) == keg::NodeType::MAP) { } else { nString overlay = keg::convert(value); auto it = m_layers.find(overlay); if (it != m_layers.end()) { texture->layers.overlay = it->second; } } } else if (key == "blendMode") { nString v = keg::convert(value); if (v == "add") { texture->blendMode = BlendType::ADD; } else if (v == "multiply") { texture->blendMode = BlendType::MULTIPLY; } else if (v == "subtract") { texture->blendMode = BlendType::SUBTRACT; } } }); nString *texNames; auto valParseFunctor = makeFunctor([&](Sender, const nString& key, keg::Node value) { nString name; // Conditional parsing, map vs value if (keg::getType(value) == keg::NodeType::MAP) { name = *blockName + std::to_string(m_generatedTextureCounter++); texture = m_texturePack->getNextFreeTexture(name); context.reader.forAllInMap(value, &texParseFunctor); } else { name = keg::convert(value); } if (key == "texture") { texNames[0] = name; } else if (key == "textureOpX") { texNames[1] = name; } else if (key == "textureOpY") { texNames[2] = name; } else if (key == "textureOpZ") { texNames[3] = name; } else if (key == "textureTop") { texNames[4] = name; } else if (key == "textureBottom") { texNames[5] = name; } else if (key == "textureFront") { texNames[6] = name; } else if (key == "textureBack") { texNames[7] = name; } else if (key == "textureLeft") { texNames[8] = name; } else if (key == "textureRight") { texNames[9] = name; } }); // Load all layers auto f = makeFunctor([&](Sender, const nString& key, keg::Node value) { BlockTextureNames tNames = {}; // So we can prioritize. nString textureNames[10]; texNames = textureNames; blockName = &key; context.reader.forAllInMap(value, &valParseFunctor); // Set textures based on names if (texNames[0].size()) { for (int i = 0; i < 6; i++) { tNames.names[i] = texNames[0]; } } if (texNames[1].size()) { tNames.names[(int)vvox::Cardinal::X_NEG] = texNames[1]; tNames.names[(int)vvox::Cardinal::X_POS] = texNames[1]; } if (texNames[2].size()) { tNames.names[(int)vvox::Cardinal::Y_NEG] = texNames[2]; tNames.names[(int)vvox::Cardinal::Y_POS] = texNames[2]; } if (texNames[3].size()) { tNames.names[(int)vvox::Cardinal::Z_NEG] = texNames[3]; tNames.names[(int)vvox::Cardinal::Z_POS] = texNames[3]; } if (texNames[4].size()) { tNames.names[(int)vvox::Cardinal::Y_POS] = texNames[4]; } if (texNames[5].size()) { tNames.names[(int)vvox::Cardinal::Y_NEG] = texNames[5]; } if (texNames[6].size()) { tNames.names[(int)vvox::Cardinal::Z_POS] = texNames[6]; } if (texNames[7].size()) { tNames.names[(int)vvox::Cardinal::Z_NEG] = texNames[7]; } if (texNames[8].size()) { tNames.names[(int)vvox::Cardinal::X_NEG] = texNames[8]; } if (texNames[9].size()) { tNames.names[(int)vvox::Cardinal::X_POS] = texNames[9]; } // Set mappings m_blockMappings[key] = tNames; }); context.reader.forAllInMap(node, &f); context.reader.dispose(); return true; } bool BlockTextureLoader::loadLayer(BlockTextureLayer& layer) { AtlasTextureDescription desc = m_texturePack->findLayer(layer.path); // Check if its already been loaded if (desc.size.x != 0) { // Already loaded so just use the desc // TODO(Ben): Worry about different methods using same file? layer.size = desc.size; layer.index.layer = desc.index; } else { vio::Path path; if (!m_texturePathResolver->resolvePath(layer.path, path)) return false; { // Get pixels for the base texture vg::ScopedBitmapResource rs(vg::ImageIO().load(path, vg::ImageIOFormat::RGBA_UI8)); // Do post processing on the layer if (!postProcessLayer(rs, layer)) return false; layer.index.layer = m_texturePack->addLayer(layer, layer.path, (color4*)rs.bytesUI8v4); } // Normal map if (layer.normalPath.size() && m_texturePathResolver->resolvePath(layer.normalPath, path)) { vg::ScopedBitmapResource rs(vg::ImageIO().load(path, vg::ImageIOFormat::RGBA_UI8)); // Do post processing on the layer if (rs.data) { layer.index.normal = m_texturePack->addLayer(layer, layer.normalPath, (color4*)rs.bytesUI8v4); } } // disp map if (layer.dispPath.size() && m_texturePathResolver->resolvePath(layer.dispPath, path)) { vg::ScopedBitmapResource rs(vg::ImageIO().load(path, vg::ImageIOFormat::RGBA_UI8)); // Do post processing on the layer if (rs.data) { layer.index.disp = m_texturePack->addLayer(layer, layer.dispPath, (color4*)rs.bytesUI8v4); } } } return true; } bool BlockTextureLoader::postProcessLayer(vg::ScopedBitmapResource& bitmap, BlockTextureLayer& layer) { // ui32 floraRows; const ui32& resolution = m_texturePack->getResolution(); // TODO(Ben): Floraheight // Helper for checking dimensions #define DIM_CHECK(w, cw, h, ch, method) \ if (bitmap.width != resolution * cw) { \ pError("Texture " + layer.path + " is " #method " but width is not " + std::to_string(cw)); \ return false; \ } \ if (bitmap.height != resolution * ch) { \ pError("Texture " + layer.path + " is " #method " but height is not " + std::to_string(ch)); \ return false; \ } // Pixels must exist if (!bitmap.data) return false; // Check that the texture is sized in units of resolution if (bitmap.width % resolution) { pError("Texture " + layer.path + " width must be a multiple of " + std::to_string(resolution)); return false; } if (bitmap.height % resolution) { pError("Texture " + layer.path + " height must be a multiple of " + std::to_string(resolution)); return false; } // Check for errors and postprocessing based on method switch (layer.method) { // Need to set up numTiles and totalWeight for RANDOM method case ConnectedTextureMethods::CONNECTED: layer.size = ui8v2(1); DIM_CHECK(width, CONNECTED_WIDTH, bitmap.height, CONNECTED_HEIGHT, CONNECTED); break; case ConnectedTextureMethods::RANDOM: layer.numTiles = bitmap.width / bitmap.height; layer.size = ui32v2(1); if (layer.weights.size() == 0) { layer.totalWeight = layer.numTiles; } else { // Need to check if there is the right number of weights if (layer.weights.size() * resolution != bitmap.width) { pError("Texture " + layer.path + " weights length must match number of columns or be empty. weights.length() = " + std::to_string(layer.weights.size()) + " but there are " + std::to_string(bitmap.width / resolution) + " columns."); return false; } } break; case ConnectedTextureMethods::GRASS: layer.size = ui8v2(1); DIM_CHECK(width, GRASS_WIDTH, bitmap.height, GRASS_HEIGHT, GRASS); break; case ConnectedTextureMethods::HORIZONTAL: layer.size.x = (ui8)(bitmap.width / resolution); layer.size.y = (ui8)(bitmap.height / resolution); DIM_CHECK(width, HORIZONTAL_WIDTH, bitmap.height, HORIZONTAL_HEIGHT, HORIZONTAL); break; case ConnectedTextureMethods::VERTICAL: layer.size.x = (ui8)(bitmap.width / resolution); layer.size.y = (ui8)(bitmap.height / resolution); DIM_CHECK(width, HORIZONTAL_HEIGHT, bitmap.height, HORIZONTAL_WIDTH, VERTICAL); break; case ConnectedTextureMethods::REPEAT: layer.size.x = (ui8)(bitmap.width / resolution); layer.size.y = (ui8)(bitmap.height / resolution); DIM_CHECK(width, layer.size.x, bitmap.height, layer.size.y, REPEAT); break; //case ConnectedTextureMethods::FLORA: // floraRows = BlockTextureLayer::getFloraRows(layer.floraHeight); // if (bitmap.height != resolution * floraRows) { // pError("Texture " + layer.path + " texture height must be equal to (maxFloraHeight^2 + maxFloraHeight) / 2 * resolution = " + // std::to_string(bitmap.height) + " but it is " + std::to_string(resolution * floraRows)); // return false; // } // // If no weights, they are all equal // if (layer.weights.size() == 0) { // layer.totalWeight = bitmap.width / resolution; // } else { // Need to check if there is the right number of weights // if (layer.weights.size() * resolution != bitmap.width) { // pError("Texture " + layer.path + " weights length must match number of columns or be empty. weights.length() = " + // std::to_string(layer.weights.size()) + " but there are " + std::to_string(bitmap.width / resolution) + " columns."); // return false; // } // } // // Tile dimensions and count // layer.size.x = bitmap.width / resolution; // layer.size.y = floraRows; // layer.numTiles = layer.size.x * layer.size.y; // break; case ConnectedTextureMethods::NONE: DIM_CHECK(bitmap.width, 1, bitmap.height, 1, NONE); break; default: break; } layer.initBlockTextureFunc(); return true; } ================================================ FILE: SoA/BlockTextureLoader.h ================================================ /// /// BlockTextureLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 16 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Loads block textures /// #pragma once #ifndef BlockTextureLoader_h__ #define BlockTextureLoader_h__ #include #include #include "BlockData.h" DECL_VG(class ScopedBitmapResource) class Block; class BlockTexturePack; class ModPathResolver; class BlockTextureLayer; struct BlockTexture; struct BlockTextureNames { nString names[6]; }; class BlockTextureLoader { public: void init(ModPathResolver* texturePathResolver, BlockTexturePack* texturePack); void loadTextureData(); void loadBlockTextures(Block& block); void dispose(); BlockTexturePack* getTexturePack() const { return m_texturePack; } private: bool loadLayerProperties(); bool loadTextureProperties(); bool loadBlockTextureMapping(); bool loadLayer(BlockTextureLayer& layer); bool postProcessLayer(vg::ScopedBitmapResource& bitmap, BlockTextureLayer& layer); std::map m_layers; std::map m_blockMappings; ModPathResolver* m_texturePathResolver = nullptr; BlockTexturePack* m_texturePack = nullptr; vio::IOManager m_iom; int m_generatedTextureCounter = 0; }; #endif // BlockTextureLoader_h__ ================================================ FILE: SoA/BlockTextureMethods.cpp ================================================ #include "stdafx.h" #include "BlockTextureMethods.h" #include #include #include #include "BlockPack.h" #include "Chunk.h" #include "ChunkMesh.h" #include "ChunkMesher.h" #include "VoxelBits.h" #include "soaUtils.h" #define GETBLOCK(a) (((*blocks)[((a) & 0x0FFF)])) // We are assuming layerIndex can be trusted to be 0 or 1 here, add asserts? #define TEXTURE_INDEX (params.layerIndex == 0 ? \ block->textures[params.faceIndex]->layers.base.indices[params.typeIndex] : \ block->textures[params.faceIndex]->layers.overlay.indices[params.typeIndex]) inline ui32 getPositionSeed(const i32v3& pos) { i32 val = ((pos.x & 0x7ff) | ((pos.y & 0x3ff) << 11) | ((pos.z & 0x7ff) << 21)); // Treat i32 bits as ui32 so we don't just truncate with a cast return *(ui32*)&val; } void BlockTextureMethods::getDefaultTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { result.size = params.blockTexInfo->size; } //Gets a random offset for use by random textures void BlockTextureMethods::getRandomTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { //TODO: MurmurHash3 const ChunkMesher* cm = params.chunkMesher; const BlockTextureLayer* blockTexInfo = params.blockTexInfo; i32v3 pos(cm->chunkVoxelPos.pos.x + cm->bx, cm->chunkVoxelPos.pos.y + cm->by, cm->chunkVoxelPos.pos.z + cm->bz); // TODO(Ben): Faster RNG? f32 r = params.blockTexInfo->totalWeight * ((f32)rand() / RAND_MAX); f32 totalWeight = 0; result.size = params.blockTexInfo->size; // TODO(Ben): Binary search? if (blockTexInfo->weights.size()) { for (ui32 i = 0; i < blockTexInfo->numTiles; i++) { totalWeight += blockTexInfo->weights[i]; if (r <= totalWeight) { result.index += i; return; } } } else { for (ui32 i = 0; i < blockTexInfo->numTiles; i++) { totalWeight += 1.0f; if (r <= totalWeight) { result.index += i; return; } } } } void BlockTextureMethods::getFloraTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { //TODO: MurmurHash3 const ChunkMesher* cm = params.chunkMesher; // i32 seed = 0; // getPositionSeed(cm->x + cm->position.x, cm->y + cm->position.y, cm->z + cm->position.z); f32 r = (f32)((/*PseudoRand(seed) +*/ 1.0) * 0.5 * params.blockTexInfo->totalWeight); f32 totalWeight = 0; const BlockTextureLayer* blockTexInfo = params.blockTexInfo; const ui16* tertiaryData = cm->tertiaryData; const int& blockIndex = cm->blockIndex; int column=0; // TODO(Ben): Binary search? if (blockTexInfo->weights.size()) { for (ui32 i = 0; i < blockTexInfo->size.x; i++) { totalWeight += blockTexInfo->weights[i]; if (r <= totalWeight) { column = i; break; } } } else { for (ui32 i = 0; i < blockTexInfo->size.x; i++) { totalWeight += 1.0f; if (r <= totalWeight) { column = i; break; } } } result.index += column; // Get the height of the current voxel int height = MIN(VoxelBits::getFloraHeight(tertiaryData[blockIndex]), cm->block->floraHeight); int yPos = height - VoxelBits::getFloraPosition(tertiaryData[blockIndex]); // Move the result to the flora of the correct height result.index += blockTexInfo->size.x * (height * height + height) / 2; // Offset by the ypos result.index += blockTexInfo->size.x * yPos; result.size = params.blockTexInfo->size; } //Gets a connected texture offset by looking at the surrounding blocks void BlockTextureMethods::getConnectedTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { const BlockPack* blocks = params.chunkMesher->blocks; int connectedOffset = 0; const int& blockIndex = params.chunkMesher->blockIndex; const int& upDir = params.upDir; const int& rightDir = params.rightDir; const int& frontDir = params.frontDir; const ui16* blockIDData = params.chunkMesher->blockData; BlockTextureIndex tex = result.index; // Top Left const Block *block = &GETBLOCK(blockIDData[blockIndex + upDir - rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x80; } // Top block = &GETBLOCK(blockIDData[blockIndex + upDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0xE0; } // Top Right block = &GETBLOCK(blockIDData[blockIndex + upDir + rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x20; } // Right block = &GETBLOCK(blockIDData[blockIndex + rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x38; } // Bottom Right block = &GETBLOCK(blockIDData[blockIndex - upDir + rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x8; } // Bottom block = &GETBLOCK(blockIDData[blockIndex - upDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0xE; } // Bottom Left block = &GETBLOCK(blockIDData[blockIndex - upDir - rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x2; } // Left block = &GETBLOCK(blockIDData[blockIndex - rightDir]); if (TEXTURE_INDEX != tex) { connectedOffset |= 0x83; } if (params.blockTexInfo->innerSeams) { // Top Front Left block = &GETBLOCK(blockIDData[blockIndex + upDir - rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x80; } // Top Front Right block = &GETBLOCK(blockIDData[blockIndex + upDir + rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x20; } // Bottom front Right block = &GETBLOCK(blockIDData[blockIndex - upDir + rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x8; } //Bottom front block = &GETBLOCK(blockIDData[blockIndex - upDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0xE; } // Bottom front Left block = &GETBLOCK(blockIDData[blockIndex - upDir - rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x2; } //Left front block = &GETBLOCK(blockIDData[blockIndex - rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x83; } //Top front block = &GETBLOCK(blockIDData[blockIndex + upDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0xE0; } //Right front block = &GETBLOCK(blockIDData[blockIndex + rightDir + frontDir]); if (block->occlude != BlockOcclusion::NONE) { connectedOffset |= 0x38; } } result.size = params.blockTexInfo->size; result.index += vg::ConnectedTextureHelper::getOffsetFull(connectedOffset); } //Gets a grass side texture offset by looking at the surrounding blocks void BlockTextureMethods::getGrassTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { const BlockPack* blocks = params.chunkMesher->blocks; int connectedOffset = 0; const int& blockIndex = params.chunkMesher->blockIndex; const int& upDir = params.upDir; const int& rightDir = params.rightDir; const int& frontDir = params.frontDir; const ui16* blockIDData = params.chunkMesher->blockData; const ChunkMesher* cm = params.chunkMesher; BlockTextureIndex tex = result.index; // Bottom Front int index = blockIndex - upDir + frontDir; int id = blockIDData[index]; const Block* block = &GETBLOCK(id); if (/*cm->levelOfDetail > 1 || */ TEXTURE_INDEX == tex) { block = &GETBLOCK(blockIDData[blockIndex]); result.index = block->textureTop->layers.base.index.layer; block->textureTop->layers.base.blockTextureFunc(params, result); block->textureTop->layers.base.getFinalColor(*params.color, cm->heightData->temperature, cm->heightData->humidity, 0); result.size = block->textureTop->layers.base.size; return; } // Left block = &GETBLOCK(blockIDData[blockIndex - rightDir]); if (TEXTURE_INDEX == tex || block->occlude == BlockOcclusion::NONE) { connectedOffset |= 0x8; // REDUNDANT if (TEXTURE_INDEX == tex) { // bottom front Left block = &GETBLOCK(blockIDData[blockIndex - upDir - rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0xC; } } } // Front left block = &GETBLOCK(blockIDData[blockIndex - rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0x8; } // Bottom left block = &GETBLOCK(blockIDData[blockIndex - upDir - rightDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0xC; } // bottom right block = &GETBLOCK(blockIDData[blockIndex - upDir + rightDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0x3; } // Right block = &GETBLOCK(blockIDData[blockIndex + rightDir]); if (TEXTURE_INDEX == tex || block->occlude == BlockOcclusion::NONE) { connectedOffset |= 0x1; if (TEXTURE_INDEX == tex) { // bottom front Right block = &GETBLOCK(blockIDData[blockIndex - upDir + rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0x3; } } } // Front right block = &GETBLOCK(blockIDData[blockIndex + rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 0x1; } result.size = params.blockTexInfo->size; result.index += vg::ConnectedTextureHelper::getOffsetSmall(connectedOffset); } void BlockTextureMethods::getVerticalTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { const BlockPack* blocks = params.chunkMesher->blocks; static int verticalOffsets[4] = { 0, 1, 3, 2 }; int connectedOffset; const int& blockIndex = params.chunkMesher->blockIndex; const int& upDir = params.upDir; const ui16* blockIDData = params.chunkMesher->blockData; const ConnectedTextureReducedMethod& rm = params.blockTexInfo->reducedMethod; BlockTextureIndex tex = result.index; if (rm == ConnectedTextureReducedMethod::NONE) { connectedOffset = 0; } else if (rm == ConnectedTextureReducedMethod::TOP) { connectedOffset = 1; } else { //BOTTOM connectedOffset = 2; } //top bit const Block *block = &GETBLOCK(blockIDData[blockIndex + upDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 2; } //bottom bit block = &GETBLOCK(blockIDData[blockIndex - upDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 1; } result.size = params.blockTexInfo->size; result.index += verticalOffsets[connectedOffset]; } void BlockTextureMethods::getHorizontalTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result) { static int horizontalOffsets[4] = { 0, 1, 3, 2 }; const BlockPack* blocks = params.chunkMesher->blocks; int connectedOffset = 0; const int& blockIndex = params.chunkMesher->blockIndex; const int& rightDir = params.rightDir; const int& frontDir = params.frontDir; const ui16* blockIDData = params.chunkMesher->blockData; BlockTextureIndex tex = result.index; //right bit const Block *block = &GETBLOCK(blockIDData[blockIndex + rightDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 1; } //left bit block = &GETBLOCK(blockIDData[blockIndex - rightDir]); if (TEXTURE_INDEX == tex) { connectedOffset |= 2; } if (params.blockTexInfo->innerSeams) { //front right bit block = &GETBLOCK(blockIDData[blockIndex + rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset &= 2; } //front left bit block = &GETBLOCK(blockIDData[blockIndex - rightDir + frontDir]); if (TEXTURE_INDEX == tex) { connectedOffset &= 1; } } result.size = params.blockTexInfo->size; result.index += horizontalOffsets[connectedOffset]; } ================================================ FILE: SoA/BlockTextureMethods.h ================================================ /// /// BlockTextureMethods.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 7 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides the texturing methods functions for block textures, /// such as connected textures and random textures. /// #pragma once #ifndef BlockTextureMethods_h__ #define BlockTextureMethods_h__ #include #include class ChunkMesher; class BlockTextureLayer; class BlockPack; typedef ui32 BlockTextureIndex; class BlockTextureMethodParams { public: void init(ChunkMesher* cm, i32 RightDir, i32 UpDir, i32 FrontDir, ui32 face, ui32 layerIndex) { chunkMesher = cm; rightDir = RightDir; upDir = UpDir; frontDir = FrontDir; faceIndex = face; this->layerIndex = layerIndex; } void set(const BlockTextureLayer* blockTextureLayer, ui32 typeIndex, ColorRGB8& Color) { blockTexInfo = blockTextureLayer; this->typeIndex = typeIndex; color = &Color; } const ChunkMesher* chunkMesher = nullptr; const BlockTextureLayer* blockTexInfo = nullptr; i32 rightDir; i32 upDir; i32 frontDir; ui32 faceIndex; ui32 layerIndex; ui32 typeIndex; ColorRGB8* color = nullptr; }; struct BlockTextureMethodData { BlockTextureIndex index; ui8v2 size; }; typedef std::function BlockTextureFunc; namespace BlockTextureMethods { void getDefaultTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getRandomTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getFloraTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getConnectedTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getGrassTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getVerticalTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); void getHorizontalTextureIndex(BlockTextureMethodParams& params, BlockTextureMethodData& result); } #endif // BlockTextureMethods_h__ ================================================ FILE: SoA/BlockTexturePack.cpp ================================================ #include "stdafx.h" #include "BlockTexturePack.h" #include "BlockPack.h" // TEMPORARY #include "BlockTexture.h" #include "SpaceSystemComponents.h" #include "SpaceSystemAssemblages.h" #include #include "Errors.h" #define CONNECTED_TILES 47 #define GRASS_TILES 9 #define HORIZONTAL_TILES 4 #define VERTICAL_TILES 4 BlockTexturePack::~BlockTexturePack() { dispose(); } void BlockTexturePack::init(ui32 resolution, ui32 maxTextures) { m_resolution = resolution; m_pageWidthPixels = m_resolution * m_stitcher.getTilesPerRow(); m_maxTextures = maxTextures; m_textures = new BlockTexture[maxTextures]; m_nextFree = 0; // Calculate max mipmap level m_mipLevels = 0; ui32 width = m_pageWidthPixels; while (width > m_stitcher.getTilesPerRow()) { width >>= 1; m_mipLevels++; } // Set up first page for default textures flagDirtyPage(0); // Allocate biome and liquid maps and init to white BlockColorMap& bmap = m_colorMaps["biome"]; memset(bmap.pixels, 255, sizeof(bmap.pixels)); BlockColorMap& lmap = m_colorMaps["liquid"]; memset(lmap.pixels, 255, sizeof(lmap.pixels)); SpaceSystemAssemblages::onAddSphericalVoxelComponent += makeDelegate(this, &BlockTexturePack::onAddSphericalVoxelComponent); } // TODO(Ben): Lock? BlockTextureIndex BlockTexturePack::addLayer(const BlockTextureLayer& layer, const nString& path, color4* pixels) { BlockTextureIndex rv = 0; // Map the texture int firstPageIndex; int lastPageIndex; switch (layer.method) { case ConnectedTextureMethods::CONNECTED: rv = m_stitcher.mapContiguous(CONNECTED_TILES); lastPageIndex = (rv + CONNECTED_TILES) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::RANDOM: rv = m_stitcher.mapContiguous(layer.numTiles); lastPageIndex = (rv + layer.numTiles) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::REPEAT: rv = m_stitcher.mapBox(layer.size.x, layer.size.y); lastPageIndex = (rv + (layer.size.y - 1) * m_stitcher.getTilesPerRow() + (layer.size.x - 1)) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::GRASS: rv = m_stitcher.mapContiguous(GRASS_TILES); lastPageIndex = (rv + GRASS_TILES) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::HORIZONTAL: rv = m_stitcher.mapContiguous(HORIZONTAL_TILES); lastPageIndex = (rv + HORIZONTAL_TILES) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::VERTICAL: rv = m_stitcher.mapContiguous(VERTICAL_TILES); lastPageIndex = (rv + VERTICAL_TILES) / m_stitcher.getTilesPerPage(); break; case ConnectedTextureMethods::FLORA: rv = m_stitcher.mapContiguous(layer.numTiles); lastPageIndex = (rv + layer.numTiles) / m_stitcher.getTilesPerPage(); break; default: rv = m_stitcher.mapSingle(); lastPageIndex = (rv + 1) / m_stitcher.getTilesPerPage(); break; } firstPageIndex = rv / m_stitcher.getTilesPerPage(); flagDirtyPage(firstPageIndex); if (lastPageIndex != firstPageIndex) flagDirtyPage(lastPageIndex); // Copy data switch (layer.method) { case ConnectedTextureMethods::CONNECTED: writeToAtlasContiguous(rv, pixels, 12, 4, CONNECTED_TILES); break; case ConnectedTextureMethods::RANDOM: writeToAtlasContiguous(rv, pixels, layer.numTiles, 1, layer.numTiles); break; case ConnectedTextureMethods::REPEAT: writeToAtlas(rv, pixels, m_resolution * layer.size.x, m_resolution * layer.size.y, 1); break; case ConnectedTextureMethods::GRASS: writeToAtlasContiguous(rv, pixels, 3, 3, GRASS_TILES); break; case ConnectedTextureMethods::HORIZONTAL: writeToAtlasContiguous(rv, pixels, HORIZONTAL_TILES, 1, HORIZONTAL_TILES); break; case ConnectedTextureMethods::VERTICAL: writeToAtlasContiguous(rv, pixels, 1, VERTICAL_TILES, VERTICAL_TILES); break; case ConnectedTextureMethods::FLORA: writeToAtlasContiguous(rv, pixels, layer.size.x, layer.size.y, layer.numTiles); break; default: writeToAtlas(rv, pixels, m_resolution, m_resolution, 1); break; } // Cache the texture description AtlasTextureDescription tex; tex.index = rv; tex.size = layer.size; tex.temp = layer; m_descLookup[path] = tex; return rv; } AtlasTextureDescription BlockTexturePack::findLayer(const nString& filePath) { auto it = m_descLookup.find(filePath); if (it != m_descLookup.end()) { return it->second; } return {}; } BlockTexture* BlockTexturePack::findTexture(const nString& filePath) { auto it = m_textureLookup.find(filePath); if (it != m_textureLookup.end()) { return &m_textures[it->second]; } return nullptr; } // Returns a pointer to the next free block texture and increments internal counter. // Will crash if called more than m_maxTextures times. BlockTexture* BlockTexturePack::getNextFreeTexture(const nString& filePath) { if (m_nextFree >= m_maxTextures) pError("m_nextFree >= m_maxTextures in BlockTexturePack::getNextFreeTexture"); m_textureLookup[filePath] = m_nextFree; return &m_textures[m_nextFree++]; } BlockColorMap* BlockTexturePack::getColorMap(const nString& path) { auto it = m_colorMaps.find(path); if (it != m_colorMaps.end()) return &it->second; return nullptr; } BlockColorMap* BlockTexturePack::setColorMap(const nString& name, const vg::BitmapResource* rs) { // Error check if (rs->width != BLOCK_COLOR_MAP_WIDTH || rs->height != BLOCK_COLOR_MAP_WIDTH) { fprintf(stderr, "Warning: Color map %s is not %d x %d\n", name.c_str(), BLOCK_COLOR_MAP_WIDTH, BLOCK_COLOR_MAP_WIDTH); fflush(stderr); } return setColorMap(name, rs->bytesUI8v3); } BlockColorMap* BlockTexturePack::setColorMap(const nString& name, const ui8v3* pixels) { // Allocate the color map BlockColorMap* colorMap = &m_colorMaps[name]; // Set its pixels for (int y = 0; y < BLOCK_COLOR_MAP_WIDTH; y++) { for (int x = 0; x < BLOCK_COLOR_MAP_WIDTH; x++) { colorMap->pixels[y][x].r = pixels[y * BLOCK_COLOR_MAP_WIDTH + x].r; colorMap->pixels[y][x].g = pixels[y * BLOCK_COLOR_MAP_WIDTH + x].g; colorMap->pixels[y][x].b = pixels[y * BLOCK_COLOR_MAP_WIDTH + x].b; } } return colorMap; } void BlockTexturePack::update() { bool needsMipmapGen = false; if (m_needsRealloc) { allocatePages(); m_needsRealloc = false; needsMipmapGen = true; // Upload all pages glBindTexture(GL_TEXTURE_2D_ARRAY, m_atlasTexture); for (size_t i = 0; i < m_pages.size(); i++) { uploadPage(i); } std::vector().swap(m_dirtyPages); } else if (m_dirtyPages.size()) { needsMipmapGen = true; // Upload dirty pages glBindTexture(GL_TEXTURE_2D_ARRAY, m_atlasTexture); for (auto& i : m_dirtyPages) { uploadPage(i); } std::vector().swap(m_dirtyPages); } if (needsMipmapGen) { glGenerateMipmap(GL_TEXTURE_2D_ARRAY); glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } } void BlockTexturePack::writeDebugAtlases() { int width = m_resolution * m_stitcher.getTilesPerRow(); int height = width; int pixelsPerPage = width * height * 4; ui8 *pixels = new ui8[width * height * 4 * m_pages.size()]; glBindTexture(GL_TEXTURE_2D_ARRAY, m_atlasTexture); glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); for (size_t i = 0; i < m_pages.size(); i++) { SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels + i * pixelsPerPage, width, height, m_resolution, 4 * width, 0xFF, 0xFF00, 0xFF0000, 0x0); SDL_SaveBMP(surface, ("atlas" + std::to_string(i) + ".bmp").c_str()); } glBindTexture(GL_TEXTURE_2D_ARRAY, 0); delete[] pixels; } void BlockTexturePack::dispose() { if (m_atlasTexture) glDeleteTextures(1, &m_atlasTexture); m_atlasTexture = 0; for (auto& p : m_pages) { delete[] p.pixels; } std::vector().swap(m_pages); m_stitcher.dispose(); std::map().swap(m_textureLookup); m_nextFree = 0; delete[] m_textures; m_textures = nullptr; SpaceSystemAssemblages::onAddSphericalVoxelComponent -= makeDelegate(this, &BlockTexturePack::onAddSphericalVoxelComponent); } nString getName(nString name) { if (name.empty()) return ""; vio::Path p(name); name = p.getLeaf(); while (name.back() != '.') name.pop_back(); name.pop_back(); return name; } void BlockTexturePack::flagDirtyPage(ui32 pageIndex) { // If we need to allocate new pages, do so if (pageIndex >= m_pages.size()) { size_t i = m_pages.size(); m_pages.resize(pageIndex + 1); for (; i < m_pages.size(); i++) { m_pages[i].pixels = new color4[m_pageWidthPixels * m_pageWidthPixels]; memset(m_pages[i].pixels, 0, m_pageWidthPixels * m_pageWidthPixels * sizeof(color4)); } m_needsRealloc = true; } m_pages[pageIndex].dirty = true; } void BlockTexturePack::allocatePages() { // Set up the storage if (!m_atlasTexture) glGenTextures(1, &m_atlasTexture); glBindTexture(GL_TEXTURE_2D_ARRAY, m_atlasTexture); // Set up all the mipmap storage ui32 width = m_pageWidthPixels; for (ui32 i = 0; i < m_mipLevels; i++) { glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA8, width, width, m_pages.size(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); width >>= 1; if (width < 1) width = 1; } // Set up tex parameters glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, (int)m_mipLevels); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LOD, (int)m_mipLevels); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); // Anisotropic filtering float anisotropy; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy); glActiveTexture(GL_TEXTURE0); // Smooth texture params // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); // Unbind glBindTexture(GL_TEXTURE_2D_ARRAY, 0); // Check if we had any errors checkGlError("BlockTexturePack::allocatePages"); } void BlockTexturePack::uploadPage(ui32 pageIndex) { glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, pageIndex, m_pageWidthPixels, m_pageWidthPixels, 1, GL_RGBA, GL_UNSIGNED_BYTE, m_pages[pageIndex].pixels); } void BlockTexturePack::writeToAtlas(BlockTextureIndex texIndex, color4* pixels, ui32 pixelWidth, ui32 pixelHeight, ui32 tileWidth) { // Get the location in the array ui32 i = texIndex % m_stitcher.getTilesPerPage(); ui32 dx = i % m_stitcher.getTilesPerRow(); ui32 dy = i / m_stitcher.getTilesPerRow(); ui32 pageIndex = texIndex / m_stitcher.getTilesPerPage(); // Temp variables to reduce multiplications ui32 destOffset; ui32 pixelsOffset; ui32 yDestOffset; ui32 yPixelsOffset; ui32 pixelsPerRow = pixelWidth * tileWidth; // Start of destination color4* dest = m_pages[pageIndex].pixels + dx * m_resolution + dy * m_resolution * m_pageWidthPixels; float alpha; // Copy the block of pixels for (ui32 y = 0; y < pixelHeight; y++) { // Calculate Y offsets yDestOffset = y * m_pageWidthPixels; yPixelsOffset = y * pixelsPerRow; // Need to do alpha blending for every pixel against a black background for (ui32 x = 0; x < pixelWidth; x++) { // Calculate offsets destOffset = yDestOffset + x; pixelsOffset = yPixelsOffset + x; // Convert 0-255 to 0-1 for alpha mult alpha = (float)pixels[pixelsOffset].a / 255.0f; // Set the colors. Add + 0.01f to make sure there isn't any rounding error when we truncate dest[destOffset].r = (ui8)((float)pixels[pixelsOffset].r * alpha + 0.01f); // R dest[destOffset].g = (ui8)((float)pixels[pixelsOffset].g * alpha + 0.01f); // G dest[destOffset].b = (ui8)((float)pixels[pixelsOffset].b * alpha + 0.01f); // B dest[destOffset].a = pixels[pixelsOffset].a; // A } } } void BlockTexturePack::writeToAtlasContiguous(BlockTextureIndex texIndex, color4* pixels, ui32 width, ui32 height, ui32 numTiles) { ui32 pixelsPerTileRow = m_resolution * m_resolution * width; ui32 n = 0; for (ui32 y = 0; y < height; y++) { for (ui32 x = 0; x < width && n < numTiles; x++, n++) { // Get pointer to source data color4* src = pixels + y * pixelsPerTileRow + x * m_resolution; writeToAtlas(texIndex++, src, m_resolution, m_resolution, width); } } } void BlockTexturePack::onAddSphericalVoxelComponent(Sender s VORB_MAYBE_UNUSED, SphericalVoxelComponent& cmp, vecs::EntityID e VORB_MAYBE_UNUSED) { // Set color maps // TODO(Ben): This assumes a single SVC! if (cmp.planetGenData->terrainColorPixels.data) { BlockColorMap* m = setColorMap("biome", &cmp.planetGenData->terrainColorPixels); // Set all layers for (ui32 i = 0; i < m_nextFree; i++) { if (m_textures[i].layers.base.colorMapPath == "biome") { m_textures[i].layers.base.colorMap = m; } if (m_textures[i].layers.overlay.colorMapPath == "biome") { m_textures[i].layers.overlay.colorMap = m; } } } if (cmp.planetGenData->liquidColorMap.id) { BlockColorMap* m = setColorMap("liquid", &cmp.planetGenData->liquidColorPixels); // Set all layers for (ui32 i = 0; i < m_nextFree; i++) { if (m_textures[i].layers.base.colorMapPath == "liquid") { m_textures[i].layers.base.colorMap = m; } if (m_textures[i].layers.overlay.colorMapPath == "liquid") { m_textures[i].layers.overlay.colorMap = m; } } } } ================================================ FILE: SoA/BlockTexturePack.h ================================================ /// /// BlockTexturePack.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 16 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// A texture pack for blocks /// #pragma once #ifndef BlockTexturePack_h__ #define BlockTexturePack_h__ #include "BlockTexture.h" #include #include #include #include #include struct SphericalVoxelComponent; DECL_VG(class BitmapResource; class Texture); struct AtlasTextureDescription { BlockTextureLayer temp; BlockTextureIndex index; ui32v2 size; }; #define BLOCK_COLOR_MAP_WIDTH 256 struct BlockColorMap { color3 pixels[BLOCK_COLOR_MAP_WIDTH][BLOCK_COLOR_MAP_WIDTH]; }; class BlockTexturePack { public: BlockTexturePack(): m_atlasTexture(0), m_resolution(0), m_pageWidthPixels(0), m_mipLevels(0), m_needsRealloc(false), m_textures(nullptr), m_nextFree(0), m_maxTextures(0) {} ~BlockTexturePack(); void init(ui32 resolution, ui32 maxTextures); // Maps the texture layer to the atlas and updates the index in layer. // Does not check if texture already exists BlockTextureIndex addLayer(const BlockTextureLayer& layer, const nString& path, color4* pixels); // Tries to find the texture index. Returns empty description on fail. AtlasTextureDescription findLayer(const nString& filePath); BlockTexture* findTexture(const nString& filePath); // Returns a pointer to the next free block texture and increments internal counter. // Will crash if called more than m_maxTextures times. BlockTexture* getNextFreeTexture(const nString& filePath); BlockTexture* getDefaultTexture() { return &m_defaultTexture; } // Gets existing color map or loads from file BlockColorMap* getColorMap(const nString& path); BlockColorMap* setColorMap(const nString& name, const vg::BitmapResource* rs); BlockColorMap* setColorMap(const nString& name, const ui8v3* pixels); // Call on GL thread. Will upload any textures that aren't yet uploaded. void update(); void writeDebugAtlases(); void dispose(); const VGTexture& getAtlasTexture() const { return m_atlasTexture; } const ui32& getResolution() const { return m_resolution; } private: VORB_NON_COPYABLE(BlockTexturePack); void flagDirtyPage(ui32 pageIndex); void allocatePages(); void uploadPage(ui32 pageIndex); void writeToAtlas(BlockTextureIndex texIndex, color4* pixels, ui32 pixelWidth, ui32 pixelHeight, ui32 tileWidth); void writeToAtlasContiguous(BlockTextureIndex texIndex, color4* pixels, ui32 width, ui32 height, ui32 numTiles); /************************************************************************/ /* Event Handlers */ /************************************************************************/ void onAddSphericalVoxelComponent(Sender s, SphericalVoxelComponent& cmp, vecs::EntityID e); struct AtlasPage { AtlasPage():pixels(nullptr), dirty(true){} color4* pixels; bool dirty; }; vvox::VoxelTextureStitcher m_stitcher; VGTexture m_atlasTexture; std::vector m_pages; ///< Cached pixel data std::vector m_dirtyPages; ///< List of dirty pages TODO(Ben): Maybe bad for multithreading std::unordered_map m_descLookup; ui32 m_resolution; ui32 m_pageWidthPixels; ui32 m_mipLevels; bool m_needsRealloc; // For cache friendly caching of textures std::map m_textureLookup; std::map m_colorMaps; BlockTexture* m_textures; ///< Storage of all block textures BlockTexture m_defaultTexture; ui32 m_nextFree; ui32 m_maxTextures; ///< Maximum possible number of unique textures with this mod config }; #endif // BlockTexturePack_h__ ================================================ FILE: SoA/BloomRenderStage.cpp ================================================ #include "stdafx.h" #include "BloomRenderStage.h" #include #include #include #include "ShaderLoader.h" #include "LoadContext.h" #include "Errors.h" #include "Vorb/ui/GameWindow.h" #define TASK_WORK 4 // (arbitrary) weight of task #define TOTAL_TASK 4 // number of tasks #define TOTAL_WORK TOTAL_TASK * TASK_WORK #define BLOOM_TEXTURE_SLOT_COLOR 0 // texture slot to bind color texture which luma info will be extracted #define BLOOM_TEXTURE_SLOT_LUMA 0 // texture slot to bind luma texture #define BLOOM_TEXTURE_SLOT_BLUR 1 // texture slot to bind blur texture float BloomRenderStage::gauss(int i, float sigma2) { return 1.0 / std::sqrt(2 * 3.14159265 * sigma2) * std::exp(-(i*i) / (2 * sigma2)); } void BloomRenderStage::init(vui::GameWindow* window, StaticLoadContext& context) { IRenderStage::init(window, context); context.addAnticipatedWork(TOTAL_WORK, TOTAL_TASK); // initialize FBOs m_fbo1.setSize(m_window->getWidth(), m_window->getHeight()); m_fbo2.setSize(m_window->getWidth(), m_window->getHeight()); m_fbo1.init(vorb::graphics::TextureInternalFormat::RGBA32F, 0, vorb::graphics::TextureFormat::RGBA, vorb::graphics::TexturePixelType::FLOAT); m_fbo2.init(vorb::graphics::TextureInternalFormat::RGBA32F, 0, vorb::graphics::TextureFormat::RGBA, vorb::graphics::TexturePixelType::FLOAT); } void BloomRenderStage::setParams(ui32 gaussianN /* = 20 */, float gaussianVariance /* = 36.0f */, float lumaThreshold /* = 0.75f */) { if (gaussianN > 50) // if a bigger radius than 50 is desired, change the size of weights array in this file and the blur shaders throw "Gaussian Radius for BloomRenderStage::setParams has to be less than 50."; m_gaussianN = gaussianN; m_gaussianVariance = gaussianVariance; m_lumaThreshold = lumaThreshold; } void BloomRenderStage::load(StaticLoadContext& context) { // luma context.addTask([&](Sender, void*) { m_programLuma = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/BloomLuma.frag"); m_programLuma.use(); glUniform1i(m_programLuma.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); m_programLuma.unuse(); context.addWorkCompleted(TOTAL_TASK); }, false); // gaussian first pass context.addTask([&](Sender, void*) { m_programGaussianFirst = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/BloomGaussianFirst.frag"); m_programGaussianFirst.use(); glUniform1i(m_programGaussianFirst.getUniform("unTexLuma"), BLOOM_TEXTURE_SLOT_LUMA); glUniform1i(m_programGaussianFirst.getUniform("unHeight"), m_window->getHeight()); glUniform1i(m_programGaussianFirst.getUniform("unGaussianN"), m_gaussianN); m_programGaussianFirst.unuse(); context.addWorkCompleted(TOTAL_TASK); }, true); // gaussian second pass context.addTask([&](Sender, void*) { m_programGaussianSecond = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/BloomGaussianSecond.frag"); m_programGaussianSecond.use(); glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); glUniform1i(m_programGaussianSecond.getUniform("unWidth"), m_window->getWidth()); glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); m_programGaussianSecond.unuse(); context.addWorkCompleted(TOTAL_TASK); }, true); // calculate gaussian weights context.addTask([&](Sender, void*) { float weights[50], sum; weights[0] = gauss(0, m_gaussianVariance); sum = weights[0]; for (ui32 i = 1; i < m_gaussianN; i++) { weights[i] = gauss(i, m_gaussianVariance); sum += 2 * weights[i]; } for (ui32 i = 0; i < m_gaussianN; i++) { weights[i] = weights[i] / sum; } m_programGaussianFirst.use(); glUniform1fv(m_programGaussianFirst.getUniform("unWeight[0]"), m_gaussianN, weights); m_programGaussianFirst.unuse(); m_programGaussianSecond.use(); glUniform1fv(m_programGaussianSecond.getUniform("unWeight[0]"), m_gaussianN, weights); m_programGaussianSecond.unuse(); context.addWorkCompleted(TOTAL_TASK); }, false); } void BloomRenderStage::hook(vg::FullQuadVBO* quad) { m_quad = quad; } void BloomRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { m_programLuma.dispose(); m_programGaussianFirst.dispose(); m_programGaussianSecond.dispose(); } void BloomRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { // get initial bound FBO and bound color texture to use it on final pass GLint initial_fbo, initial_texture; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &initial_fbo); glGetIntegerv(GL_TEXTURE_BINDING_2D, &initial_texture); // color texture should be bound on GL_TEXTURE0 slot // luma pass rendering on temporary FBO 1 m_fbo1.use(); glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); glBindTexture(GL_TEXTURE_2D, initial_texture); render(BLOOM_RENDER_STAGE_LUMA); m_fbo1.unuse(m_window->getWidth(), m_window->getHeight()); // first gaussian blur pass rendering on temporary FBO 2 m_fbo2.use(); glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_LUMA); m_fbo1.bindTexture(); render(BLOOM_RENDER_STAGE_GAUSSIAN_FIRST); m_fbo2.unuse(m_window->getWidth(), m_window->getHeight()); // second gaussian blur pass rendering on initially FBO glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); glBindTexture(GL_TEXTURE_2D, initial_texture); glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_BLUR); m_fbo2.bindTexture(); glBindFramebuffer(GL_FRAMEBUFFER, initial_fbo); render(BLOOM_RENDER_STAGE_GAUSSIAN_SECOND); } void BloomRenderStage::render(BloomRenderStagePass stage) { switch (stage) { // luma case BLOOM_RENDER_STAGE_LUMA: m_programLuma.use(); m_programLuma.enableVertexAttribArrays(); glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); m_programLuma.disableVertexAttribArrays(); m_programLuma.unuse(); break; // first gaussian pass case BLOOM_RENDER_STAGE_GAUSSIAN_FIRST: m_programGaussianFirst.use(); m_programGaussianFirst.enableVertexAttribArrays(); glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); m_programGaussianFirst.disableVertexAttribArrays(); m_programGaussianFirst.unuse(); break; // second gaussian pass case BLOOM_RENDER_STAGE_GAUSSIAN_SECOND: m_programGaussianSecond.use(); m_programGaussianSecond.enableVertexAttribArrays(); glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); m_programGaussianSecond.disableVertexAttribArrays(); m_programGaussianSecond.unuse(); break; } } ================================================ FILE: SoA/BloomRenderStage.h ================================================ /// /// BloomRenderStage.h /// Seed of Andromeda /// /// Created by Isaque Dutra on 2 June 2015 /// Copyright 2015 Regrowth Studios /// MIT License /// /// This file implements a bloom render stage for /// MainMenuRenderer. /// #pragma once #ifndef BloomRenderStage_h__ #define BloomRenderStage_h__ #include #include #include #include "ShaderLoader.h" #include "LoadContext.h" #include "IRenderStage.h" typedef enum { BLOOM_RENDER_STAGE_LUMA, BLOOM_RENDER_STAGE_GAUSSIAN_FIRST, BLOOM_RENDER_STAGE_GAUSSIAN_SECOND } BloomRenderStagePass; class BloomRenderStage : public IRenderStage { public: void init(vui::GameWindow* window, StaticLoadContext& context) override; void setParams(ui32 gaussianN = 20, float gaussianVariance = 36.0f, float lumaThreshold = 0.75f); void load(StaticLoadContext& context) override; void hook(vg::FullQuadVBO* quad); void dispose(StaticLoadContext& context) override; /// Draws the render stage void render(const Camera* camera = nullptr) override; private: float gauss(int i, float sigma2); void render(BloomRenderStagePass stage); vg::GLProgram m_programLuma, m_programGaussianFirst, m_programGaussianSecond; vg::FullQuadVBO* m_quad; ///< For use in processing through data vg::GLRenderTarget m_fbo1, m_fbo2; ui32 m_gaussianN; ///< Threshold for filtering image luma for bloom bluring float m_gaussianVariance; ///< Radius number for gaussian blur. Must be less than 50. float m_lumaThreshold; ///< Gaussian variance for blur pass }; #endif // BloomRenderStage_h__ ================================================ FILE: SoA/CAEngine.cpp ================================================ #include "stdafx.h" #include "CAEngine.h" #include #include "BlockPack.h" #include "Chunk.h" #include "ChunkUpdater.h" #include "GameManager.h" #include "VoxelUtils.h" // TODO: Do we want this as is? If so reimplement and remove VORB_UNUSED tags. CaPhysicsTypeDict CaPhysicsType::typesCache; CaPhysicsTypeList CaPhysicsType::typesArray; KEG_ENUM_DEF(CAAlgorithm, CAAlgorithm, kt) { kt.addValue("none", CAAlgorithm::NONE); kt.addValue("liquid", CAAlgorithm::LIQUID); kt.addValue("powder", CAAlgorithm::POWDER); } KEG_TYPE_DEF_SAME_NAME(CaPhysicsData, kt) { kt.addValue("updateRate", keg::Value::basic(offsetof(CaPhysicsData, updateRate), keg::BasicType::UI32)); kt.addValue("liquidLevels", keg::Value::basic(offsetof(CaPhysicsData, liquidLevels), keg::BasicType::UI32)); kt.addValue("algorithm", keg::Value::custom(offsetof(CaPhysicsData, alg), "CA_ALGORITHM", true)); } bool CaPhysicsType::update() { _ticks++; if (_ticks == _data.updateRate * HALF_CA_TICK_RES) { _ticks = 0; _isEven = !_isEven; return true; } return false; } bool CaPhysicsType::loadFromYml(const nString& filePath, const vio::IOManager* ioManager) { // Load the file nString fileData; ioManager->readFileToString(filePath.c_str(), fileData); if (fileData.length()) { if (keg::parse(&_data, fileData.c_str(), "CaPhysicsData") == keg::Error::NONE) { CaPhysicsType::typesCache[filePath] = this; _caIndex = typesArray.size(); typesArray.push_back(this); } else { return false; } } else { return false; } return true; } void CaPhysicsType::clearTypes() { // Clear CA physics cache for (auto& it : CaPhysicsType::typesCache) { delete it.second; } CaPhysicsType::typesCache.clear(); typesArray.clear(); } CAEngine::CAEngine(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED) : _chunk(nullptr) // m_chunkManager(chunkManager), // m_physicsEngine(physicsEngine) { memset(_blockUpdateFlagList, 0, sizeof(_blockUpdateFlagList)); //temporary _lowIndex = 0; // TODO(Ben): yeahhhhhh.... _range = 100; _highIndex = _lowIndex + _range - 1; } void CAEngine::updateSpawnerBlocks(bool powders VORB_UNUSED) { //_lockedChunk = nullptr; //int spawnerVal; //int sinkVal; //int c; //f64v3 physicsBlockPos; //std::vector &activeBlocks = _chunk->spawnerBlocks; //Chunk *bottom = _chunk->bottom; //for (size_t i = 0; i < activeBlocks.size();){ // c = activeBlocks[i]; // const Block &block = _chunk->getBlock(c); // spawnerVal = block.spawnerVal; // sinkVal = block.sinkVal; // if (spawnerVal == 0 && sinkVal == 0){ // activeBlocks[i] = activeBlocks.back(); // activeBlocks.pop_back(); // continue; // } // if (spawnerVal){ // if (vvox::getBottomBlockData(_chunk, _lockedChunk, c) == 0){ // if (c >= CHUNK_LAYER){ // c = c - CHUNK_LAYER; // if (spawnerVal >= LOWWATER) { // ChunkUpdater::placeBlock(_chunk, _lockedChunk, c, spawnerVal); // } else if (powders){ // physicsBlockPos = f64v3((double)_chunk->voxelPosition.x + c%CHUNK_WIDTH + 0.5, (double)_chunk->voxelPosition.y + c / CHUNK_LAYER, (double)_chunk->voxelPosition.z + (c%CHUNK_LAYER) / CHUNK_WIDTH + 0.5); // m_physicsEngine->addPhysicsBlock(physicsBlockPos, spawnerVal); // } // } else if (bottom && bottom->isAccessible){ // c = c - CHUNK_LAYER + CHUNK_SIZE; // if (spawnerVal >= LOWWATER){ // ChunkUpdater::placeBlockSafe(bottom, _lockedChunk, c, spawnerVal); // } else if (powders){ // physicsBlockPos = f64v3((double)bottom->voxelPosition.x + c%CHUNK_WIDTH + 0.5, (double)bottom->voxelPosition.y + c / CHUNK_LAYER, (double)bottom->voxelPosition.z + (c%CHUNK_LAYER) / CHUNK_WIDTH + 0.5); // m_physicsEngine->addPhysicsBlock(physicsBlockPos, spawnerVal); // } // } // } // } // if (sinkVal){ // if (GETBLOCKID(vvox::getTopBlockData(_chunk, _lockedChunk, c)) == sinkVal){ // if (c + CHUNK_LAYER < CHUNK_SIZE){ // c = c + CHUNK_LAYER; // _chunk->setBlockDataSafe(_lockedChunk, c, NONE); //TODO: This is incorrect, should call RemoveBlock or something similar // ChunkUpdater::addBlockToUpdateList(_chunk, _lockedChunk, c); // _chunk->changeState(ChunkStates::MESH); // } else if (_chunk->top && _chunk->top->isAccessible){ // c = c + CHUNK_LAYER - CHUNK_SIZE; // _chunk->top->setBlockDataSafe(_lockedChunk, c, NONE); // ChunkUpdater::addBlockToUpdateList(_chunk->top, _lockedChunk, c); // _chunk->top->changeState(ChunkStates::MESH); // } // } // } // i++; //} //if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; //} } void CAEngine::updateLiquidBlocks(int caIndex VORB_UNUSED) { //_lockedChunk = nullptr; //vvox::swapLockedChunk(_chunk, _lockedChunk); //std::vector& activeUpdateList = _chunk->activeUpdateList; //std::vector *blockUpdateList = &_chunk->blockUpdateList[caIndex << 1]; //int actv = activeUpdateList[caIndex]; //int size = blockUpdateList[actv].size(); //if (size == 0) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; // return; //} //activeUpdateList[caIndex] = !(activeUpdateList[caIndex]); //switch to other list //int c, blockID; //Uint32 i; //for (i = 0; i < blockUpdateList[actv].size(); i++){ // c = blockUpdateList[actv][i]; // if (_blockUpdateFlagList[c] == 0){ // _usedUpdateFlagList.push_back(c); // _blockUpdateFlagList[c] = 1; // blockID = _chunk->getBlockID(c); // if (blockID >= _lowIndex) liquidPhysics(c, blockID); // } //} //blockUpdateList[actv].clear(); //for (Uint32 i = 0; i < _usedUpdateFlagList.size(); i++){ // _blockUpdateFlagList[_usedUpdateFlagList[i]] = 0; //} //_usedUpdateFlagList.clear(); //if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; //} } void CAEngine::updatePowderBlocks(int caIndex VORB_UNUSED) { // _lockedChunk = nullptr; // std::vector& activeUpdateList = _chunk->activeUpdateList; // std::vector *blockUpdateList = &_chunk->blockUpdateList[caIndex << 1]; // int actv = activeUpdateList[caIndex]; // Uint32 size = blockUpdateList[actv].size(); // if (size != 0){ // activeUpdateList[caIndex] = !(activeUpdateList[caIndex]); //switch to other list // int b; // Uint32 i; // for (i = 0; i < size; i++){ //powder // b = blockUpdateList[actv][i]; // if (_blockUpdateFlagList[b] == 0){ // _usedUpdateFlagList.push_back(b); // _blockUpdateFlagList[b] = 1; // powderPhysics(b); // } // } // blockUpdateList[actv].clear(); // for (i = 0; i < _usedUpdateFlagList.size(); i++){ // _blockUpdateFlagList[_usedUpdateFlagList[i]] = 0; // } // _usedUpdateFlagList.clear(); // } //// blockUpdateList = _chunk->blockUpdateList[2]; //// actv = activeUpdateList[2]; //// size = blockUpdateList[actv].size(); // //if (size != 0){ // // activeUpdateList[2] = !(activeUpdateList[2]); // // int b; // // Uint32 i; // // for (i = 0; i < size; i++){ //snow // // b = blockUpdateList[actv][i]; // // if (_blockUpdateFlagList[b] == 0){ // // _usedUpdateFlagList.push_back(b); // // _blockUpdateFlagList[b] = 1; // // if (_chunk->getBlock(b).physicsProperty == P_SNOW){ // // powderPhysics(b); // // } // // } // // } // // blockUpdateList[actv].clear(); // // for (i = 0; i < _usedUpdateFlagList.size(); i++){ // // _blockUpdateFlagList[_usedUpdateFlagList[i]] = 0; // // } // // _usedUpdateFlagList.clear(); // //} // if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; // } } void CAEngine::liquidPhysics(i32 startBlockIndex VORB_UNUSED, i32 startBlockID VORB_UNUSED) { //// Helper function //#define IS_LIQUID(b) ((b) >= _lowIndex && (b) < _highIndex) //i32v3 pos = getPosFromBlockIndex(startBlockIndex); //Chunk* owner; //i32 nextIndex; //i32 nextCoord; //i32 blockID; //i32 diff; //i32 index = 0; //Chunk* adjOwners[4] = {nullptr, nullptr, nullptr, nullptr}; //Chunk* adjAdjOwners[4] = { nullptr, nullptr, nullptr, nullptr }; //i32 adjIndices[4]; //i32 adjAdjIndices[4]; //i32 diffs[4]; //bool hasChanged = false; //bool inFrustum = (!_chunk->mesh || _chunk->mesh->inFrustum); //const i32v3 &position = _chunk->voxelPosition; //// Get the block index and owner for the bottom chunk //if (pos.y > 0){ // nextIndex = startBlockIndex - CHUNK_LAYER; // owner = _chunk; //} else if (_chunk->bottom && _chunk->bottom->isAccessible){ // nextIndex = startBlockIndex + CHUNK_SIZE - CHUNK_LAYER; // owner = _chunk->bottom; //} else{ // return; //} ////Get the block ID //blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); ////If we are falling on an air block //if (blockID == NONE){ // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, startBlockID); // ChunkUpdater::removeBlockFromLiquidPhysicsSafe(_chunk, _lockedChunk, startBlockIndex); // ChunkUpdater::updateNeighborStates(_chunk, pos, ChunkStates::WATERMESH); // if (owner != _chunk) ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // if (startBlockID > _lowIndex + 10 && inFrustum) particleEngine.addParticles(m_chunkManager, 1, f64v3(position.x + pos.x, position.y + pos.y - 1.0, position.z + pos.z), 0, 0.1, 16665, 1111, f32v4(255.0f, 255.0f, 255.0f, 255.0f), Blocks[blockID].particleTex, 0.5f, 8); // return; //} else if (IS_LIQUID(blockID)) { //If we are falling on the same liquid // //how much empty space there is // diff = _highIndex - blockID; // //if we cant completely fill in the empty space, fill it in as best we can // if (startBlockID - _lowIndex + 1 > diff){ // startBlockID -= diff; // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, blockID + diff); // if (diff > 10 && inFrustum) particleEngine.addParticles(m_chunkManager, 1, f64v3(position.x + pos.x, position.y + pos.y - 1.0, position.z + pos.z), 0, 0.1, 16665, 1111, f32v4(255.0f, 255.0f, 255.0f, 255.0f), Blocks[blockID].particleTex, 0.5f, 8); // hasChanged = 1; // // if (owner != _chunk) { // owner->changeState(ChunkStates::WATERMESH); // ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // } // } else { //otherwise ALL liquid falls and we are done // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, blockID + (startBlockID - _lowIndex + 1)); // ChunkUpdater::removeBlockFromLiquidPhysicsSafe(_chunk, _lockedChunk, startBlockIndex); // ChunkUpdater::updateNeighborStates(_chunk, pos, ChunkStates::WATERMESH); // if (owner != _chunk) ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // if (startBlockID > _lowIndex + 10 && inFrustum) particleEngine.addParticles(m_chunkManager, 1, f64v3(position.x + pos.x, position.y + pos.y - 1.0, position.z + pos.z), 0, 0.1, 16665, 1111, f32v4(255.0f, 255.0f, 255.0f, 255.0f), Blocks[blockID].particleTex, 0.5f, 8); // return; // } //} else if (Blocks[blockID].waterBreak) { //destroy a waterBreak block, such as flora // ChunkUpdater::removeBlock(m_chunkManager, m_physicsEngine, owner, _lockedChunk, nextIndex, true); // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, startBlockID); // ChunkUpdater::removeBlockFromLiquidPhysicsSafe(_chunk, _lockedChunk, startBlockIndex); // ChunkUpdater::updateNeighborStates(_chunk, pos, ChunkStates::WATERMESH); // if (owner != _chunk) ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // if (startBlockID > _lowIndex + 10 && inFrustum) particleEngine.addParticles(m_chunkManager, 1, f64v3(position.x + pos.x, position.y + pos.y - 1.0, position.z + pos.z), 0, 0.1, 16665, 1111, f32v4(255.0f, 255.0f, 255.0f, 255.0f), Blocks[blockID].particleTex, 0.5f, 8); // return; //} ////Left Direction //if (pos.x > 0) { // nextIndex = startBlockIndex - 1; // owner = _chunk; // nextCoord = pos.x - 1; //} else if (_chunk->left && _chunk->left->isAccessible) { // nextIndex = startBlockIndex - 1 + CHUNK_WIDTH; // owner = _chunk->left; // nextCoord = CHUNK_WIDTH - 1; //} else { // return; //} //blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); //if (blockID == NONE || Blocks[blockID].waterBreak){ //calculate diffs // diffs[index] = (startBlockID - (_lowIndex - 1)); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} else if (IS_LIQUID(blockID)){ //tmp CANT FLOW THOUGH FULL WATER // // if (nextCoord > 0){ //extra flow. its the secret! // adjAdjIndices[index] = nextIndex - 1; // adjAdjOwners[index] = owner; // } else if (owner->left && owner->left->isAccessible){ // adjAdjIndices[index] = nextIndex + CHUNK_WIDTH - 1; // adjAdjOwners[index] = owner->left; // } // diffs[index] = (startBlockID - blockID); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} ////Back Direction //if (pos.z > 0) { // nextIndex = startBlockIndex - CHUNK_WIDTH; // owner = _chunk; // nextCoord = pos.z - 1; //} else if (_chunk->back && _chunk->back->isAccessible) { // nextIndex = startBlockIndex - CHUNK_WIDTH + CHUNK_LAYER; // owner = _chunk->back; // nextCoord = CHUNK_WIDTH - 1; //} else { // return; //} //blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); //if (blockID == NONE || Blocks[blockID].waterBreak){ //calculate diffs // diffs[index] = (startBlockID - (_lowIndex - 1)); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} else if (IS_LIQUID(blockID)){ //tmp CANT FLOW THOUGH FULL WATER // if (nextCoord > 0){ //extra flow. its the secret! // adjAdjIndices[index] = nextIndex - CHUNK_WIDTH; // adjAdjOwners[index] = owner; // } else if (owner->back && owner->back->isAccessible){ // adjAdjIndices[index] = nextIndex - CHUNK_WIDTH + CHUNK_LAYER; // adjAdjOwners[index] = owner->back; // } // diffs[index] = (startBlockID - blockID); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} ////Right Direction //if (pos.x < CHUNK_WIDTH - 1) { // nextIndex = startBlockIndex + 1; // owner = _chunk; // nextCoord = pos.x + 1; //} else if (_chunk->right && _chunk->right->isAccessible) { // nextIndex = startBlockIndex + 1 - CHUNK_WIDTH; // owner = _chunk->right; // nextCoord = 0; //} else { // return; //} //blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); //if (blockID == NONE || Blocks[blockID].waterBreak){ //calculate diffs // diffs[index] = (startBlockID - (_lowIndex - 1)); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} else if (IS_LIQUID(blockID)){ //tmp CANT FLOW THOUGH FULL WATER // if (nextCoord < CHUNK_WIDTH - 1){ //extra flow. its the secret! // adjAdjIndices[index] = nextIndex + 1; // adjAdjOwners[index] = owner; // } else if (owner->right && owner->right->isAccessible){ // adjAdjIndices[index] = nextIndex + 1 - CHUNK_WIDTH; // adjAdjOwners[index] = owner->right; // } // diffs[index] = (startBlockID - blockID); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} ////Front Direction //if (pos.z < CHUNK_WIDTH - 1) { // nextIndex = startBlockIndex + CHUNK_WIDTH; // owner = _chunk; // nextCoord = pos.z + 1; //} else if (_chunk->front && _chunk->front->isAccessible) { // nextIndex = startBlockIndex + CHUNK_WIDTH - CHUNK_LAYER; // owner = _chunk->front; // nextCoord = 0; //} else { // return; //} //blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); //if (blockID == NONE || Blocks[blockID].waterBreak){ //calculate diffs // diffs[index] = (startBlockID - (_lowIndex - 1)); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} else if (IS_LIQUID(blockID)){ //tmp CANT FLOW THOUGH FULL WATER // if (nextCoord < CHUNK_WIDTH - 1){ //extra flow. its the secret! // adjAdjIndices[index] = nextIndex + CHUNK_WIDTH; // adjAdjOwners[index] = owner; // } else if (owner->front && owner->front->isAccessible){ // adjAdjIndices[index] = nextIndex + CHUNK_WIDTH - CHUNK_LAYER; // adjAdjOwners[index] = owner->front; // } // diffs[index] = (startBlockID - blockID); // adjOwners[index] = owner; // adjIndices[index++] = nextIndex; //} ////Spread the liquid //int numAdj = index + 1; //for (int i = 0; i < index; i++){ // nextIndex = adjIndices[i]; // owner = adjOwners[i]; // diff = diffs[i] / numAdj; // //TODO(Ben): cache this instead // blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); // if (diff > 0){ // //diff /= num; // if (diff < (startBlockID - _lowIndex + 1)){ // if (blockID == NONE){ // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, _lowIndex - 1 + diff); // } else if (Blocks[blockID].waterBreak){ // ChunkUpdater::removeBlock(m_chunkManager, m_physicsEngine, owner, _lockedChunk, nextIndex, true); // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, _lowIndex - 1 + diff); // } else{ // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, blockID + diff); // } // // startBlockID -= diff; // if (diff > 10 && inFrustum) particleEngine.addParticles(m_chunkManager, 1, f64v3(position.x + pos.x, position.y + pos.y, position.z + pos.z), 0, 0.1, 16665, 1111, f32v4(255.0f, 255.0f, 255.0f, 255.0f), Blocks[blockID].particleTex, 0.5f, 8); // // if (owner != _chunk) { // owner->changeState(ChunkStates::WATERMESH); // ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // } // hasChanged = 1; // } // } //} ////Extra movement for more realistic flow //for (int i = 0; i < index; i++) { // if (adjAdjOwners[i]){ // owner = adjAdjOwners[i]; // nextIndex = adjAdjIndices[i]; // blockID = owner->getBlockIDSafe(_lockedChunk, nextIndex); // if (blockID == NONE && startBlockID > _lowIndex){ // diff = (startBlockID - _lowIndex + 1) / 2; // startBlockID -= diff; // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, _lowIndex - 1 + diff); // if (owner != _chunk) { // owner->changeState(ChunkStates::WATERMESH); // ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // } // hasChanged = 1; // } else if (blockID >= _lowIndex && blockID < startBlockID - 1){ // diff = (startBlockID - blockID) / 2; // ChunkUpdater::placeBlockFromLiquidPhysics(owner, _lockedChunk, nextIndex, blockID + diff); // startBlockID -= diff; // // if (owner != _chunk) { // owner->changeState(ChunkStates::WATERMESH); // ChunkUpdater::updateNeighborStates(owner, nextIndex, ChunkStates::WATERMESH); // } // hasChanged = 1; // } // } //} //if (hasChanged) { // ChunkUpdater::placeBlockFromLiquidPhysicsSafe(_chunk, _lockedChunk, startBlockIndex, startBlockID); // _chunk->changeState(ChunkStates::WATERMESH); // ChunkUpdater::updateNeighborStates(_chunk, pos, ChunkStates::WATERMESH); //} } void CAEngine::powderPhysics(int blockIndex VORB_UNUSED) { //// Directional constants //#define LEFT 0 //#define RIGHT 1 //#define FRONT 2 //#define BACK 3 //// Every permutation of 0-3 for random axis directions //#define DIRS_SIZE 96 //static const int DIRS[DIRS_SIZE] = { 0, 1, 2, 3, 0, 1, 3, 2, 0, 2, 3, 1, 0, 2, 1, 3, 0, 3, 2, 1, 0, 3, 1, 2, // 1, 0, 2, 3, 1, 0, 3, 2, 1, 2, 0, 3, 1, 2, 3, 0, 1, 3, 2, 0, 1, 3, 0, 2, // 2, 0, 1, 3, 2, 0, 3, 1, 2, 1, 3, 0, 2, 1, 0, 3, 2, 3, 0, 1, 2, 3, 1, 0, // 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 2, 0, 3, 1, 0, 2, 3, 2, 0, 1, 3, 2, 1, 0 }; //int blockData = _chunk->getBlockDataSafe(_lockedChunk, blockIndex); //int nextBlockData, nextBlockIndex; //// Make sure this is a powder block //if (GETBLOCK(blockData).caAlg != CA_ALGORITHM::POWDER) return; //Chunk* nextChunk; //i32v3 pos = getPosFromBlockIndex(blockIndex); //// *** Falling in Y direction *** //// Get bottom block //nextBlockData = vvox::getBottomBlockData(_chunk, _lockedChunk, blockIndex, pos.y, nextBlockIndex, nextChunk); //// Check for edge //if (nextBlockData == -1) return; //// Since getXXXBlockData may lock a chunk, we need this //_lockedChunk = nextChunk; //// Get block info //const Block& bottomBlock = GETBLOCK(nextBlockData); //// Check if we can move down //if (bottomBlock.isCrushable) { // // Move down and crush block // ChunkUpdater::placeBlock(nextChunk, _lockedChunk, nextBlockIndex, blockData); // ChunkUpdater::removeBlockSafe(m_chunkManager, m_physicsEngine, _chunk, _lockedChunk, blockIndex, false); // return; //} else if (bottomBlock.powderMove) { // // Move down and swap places // ChunkUpdater::placeBlock(nextChunk, _lockedChunk, nextBlockIndex, blockData); // ChunkUpdater::placeBlockSafe(_chunk, _lockedChunk, blockIndex, nextBlockData); // return; //} else if (bottomBlock.caAlg != CA_ALGORITHM::POWDER) { // // We can only slide on powder, so if its not powder, we return. // return; //} //// We use _dirIndex to avoid any calls to rand(). We dont get truly random direction but //// it appears random. //#define NUM_AXIS 4 //_dirIndex += NUM_AXIS; //// Wrap back to zero //if (_dirIndex == DIRS_SIZE) _dirIndex = 0; //// Loop through our 4 "random" directions //for (int i = _dirIndex; i < _dirIndex + NUM_AXIS; i++) { // // Get the neighbor block in the direction // switch (DIRS[i]) { // case LEFT: // // Only lock the chunk if we know we aren't about to go to a neighbor // if (pos.x != 0) vvox::swapLockedChunk(_chunk, _lockedChunk); // nextBlockData = vvox::getLeftBlockData(_chunk, _lockedChunk, blockIndex, pos.x, // nextBlockIndex, nextChunk); // break; // case RIGHT: // if (pos.x != CHUNK_WIDTH - 1) vvox::swapLockedChunk(_chunk, _lockedChunk); // nextBlockData = vvox::getRightBlockData(_chunk, _lockedChunk, blockIndex, pos.x, // nextBlockIndex, nextChunk); // break; // case BACK: // if (pos.z != 0) vvox::swapLockedChunk(_chunk, _lockedChunk); // nextBlockData = vvox::getBackBlockData(_chunk, _lockedChunk, blockIndex, pos.z, // nextBlockIndex, nextChunk); // break; // case FRONT: // if (pos.z != CHUNK_WIDTH - 1) vvox::swapLockedChunk(_chunk, _lockedChunk); // nextBlockData = vvox::getFrontBlockData(_chunk, _lockedChunk, blockIndex, pos.z, // nextBlockIndex, nextChunk); // break; // } // // Check for edge // if (nextBlockData == -1) return; // // Since getXXXBlockData may lock a chunk, we need this // _lockedChunk = nextChunk; // // Check bottom // const Block& diagonalBlock = GETBLOCK(vvox::getBottomBlockData(nextChunk, _lockedChunk, // nextBlockIndex)); // // We only move to the side if we can fall down the next update // if (diagonalBlock.powderMove || diagonalBlock.isCrushable) { // // Get block info // const Block& nextBlock = GETBLOCK(nextBlockData); // // Check if we can move // if (nextBlock.isCrushable) { // // Move and crush block // ChunkUpdater::placeBlockSafe(nextChunk, _lockedChunk, nextBlockIndex, blockData); // ChunkUpdater::removeBlockSafe(m_chunkManager, m_physicsEngine, _chunk, _lockedChunk, blockIndex, false); // return; // } else if (nextBlock.powderMove) { // // Move and swap places // ChunkUpdater::placeBlockSafe(nextChunk, _lockedChunk, nextBlockIndex, blockData); // ChunkUpdater::placeBlockSafe(_chunk, _lockedChunk, blockIndex, nextBlockData); // return; // } // } //} } ================================================ FILE: SoA/CAEngine.h ================================================ #pragma once #include #include #include "CellularAutomataTask.h" #include "Constants.h" #include "LiquidData.h" DECL_VIO(class IOManager) class ChunkManager; class Chunk; class PhysicsEngine; /// Resolution of CA updates in frames #define CA_TICK_RES 4 /// Should be half of CA_TICK_RES #define HALF_CA_TICK_RES 2 class CaPhysicsData { public: ui32 liquidLevels = 0; ///< Number of block IDs reserved for liquid ui32 updateRate = 0; ///< Update speed of the CA CAAlgorithm alg; ///< CA algorithm to use }; KEG_TYPE_DECL(CaPhysicsData); typedef std::map CaPhysicsTypeDict; typedef std::vector CaPhysicsTypeList; class CaPhysicsType { public: /// Updates the type. /// @return true if it this physics type should simulate bool update(); /// Loads the data from a yml file /// @param filePath: path of the yml file /// @param ioManager: IOManager that will read the file /// @return true on success bool loadFromYml(const nString& filePath, const vio::IOManager* ioManager); // Getters const int& getCaIndex() const { return _caIndex; } const ui32& getUpdateRate() const { return _data.updateRate; } const CAAlgorithm& getCaAlg() const { return _data.alg; } const bool& getIsEven() const { return _isEven; } // Static functions /// Gets the number of CA types currently cached static int getNumCaTypes() { return typesArray.size(); } /// Clears all the cached types static void clearTypes(); static CaPhysicsTypeList typesArray; ///< Stores cached types static CaPhysicsTypeDict typesCache; ///< Stores cached types mapped to filepath private: CaPhysicsData _data; ///< The algorithm specific data int _caIndex; ///< index into typesArray bool _isEven = false; ///< Used for optimization, only update odd or even chunks at any time ui32 _ticks = 0; ///< Counts the ticks }; class CAEngine { public: CAEngine(ChunkManager* chunkManager, PhysicsEngine* physicsEngine); void setChunk(Chunk* chunk) { _chunk = chunk; } void updateSpawnerBlocks(bool powders); void updateLiquidBlocks(int caIndex); void updatePowderBlocks(int caIndex); private: void liquidPhysics(i32 startBlockIndex, i32 b); void powderPhysics(i32 c); // i32 _dirIndex; i32 _lowIndex; i32 _range; i32 _highIndex; std::vector _usedUpdateFlagList; bool _blockUpdateFlagList[CHUNK_SIZE]; Chunk* _chunk; // Chunk* _lockedChunk // ChunkManager* m_chunkManager; // PhysicsEngine* m_physicsEngine; }; ================================================ FILE: SoA/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) cmake_policy(SET CMP0054 NEW) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) if (APPLE) message("\nRun the build script: build.sh\n") endif (APPLE) message(FATAL_ERROR "You don't want to configure in the source folder!") endif() #project("SoA") list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") enable_testing() option(TARGET_CXX_17 OFF) if(TARGET_CXX_17) set(CMAKE_CXX_STANDARD 17) else() set(CMAKE_CXX_STANDARD 14) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") option(USING_GDB "Are we using gdb to debug?" On) option(EXTRA_DEBUG "Should we add extra debug symbols?" On) # We could move OPTIMISE_ON_DEBUG outside and support MSVC no doubt. option(OPTIMISE_ON_DEBUG "Should we optimise a debug target?" On) set(warnings "-Wall -Wextra -Wno-unknown-pragmas -Wno-strict-aliasing") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # This warning only exists for GCC. set(warnings "${warnings} -Wno-class-memaccess") endif() ADD_DEFINITIONS( # -std=c++11 # -std=c++0x # Other flags ${warnings} ) set(CMAKE_CXX_FLAGS_RELEASE "-O3") if (${USING_GDB}) if (${EXTRA_DEBUG}) set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3") else() set(CMAKE_CXX_FLAGS_DEBUG "-ggdb") endif() else() if (${EXTRA_DEBUG}) set(CMAKE_CXX_FLAGS_DEBUG "-g3") else() set(CMAKE_CXX_FLAGS_DEBUG "-g") endif() endif () if (${OPTIMISE_ON_DEBUG}) # This is supported by both Clang and GCC as long as we are up-to-date. # Should be fine going back as far as Ubuntu 17.10, and deffo fine on Arch. set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(warnings "/W4 /WX /EHsc") endif() IF(APPLE) INCLUDE_DIRECTORIES ( /System/Library/Frameworks ) FIND_LIBRARY(COCOA_LIBRARY Cocoa) FIND_LIBRARY(GLUT_LIBRARY GLUT ) FIND_LIBRARY(OpenGL_LIBRARY OpenGL ) MARK_AS_ADVANCED (COCOA_LIBRARY GLUT_LIBRARY OpenGL_LIBRARY) SET(EXTRA_LIBS ${COCOA_LIBRARY} ${GLUT_LIBRARY} ${OpenGL_LIBRARY}) ENDIF (APPLE) # Open GL find_package(OpenGL) if (OPENGL_FOUND) include_directories(SYSTEM ${OPENGL_INCLUDE_DIRS}) endif (OPENGL_FOUND) ## SDL #find_package(SDL) #if (SDL_FOUND) # include_directories(SYSTEM ${SDL_INCLUDE_DIRS}) #endif (SDL_FOUND) # ## Glew #find_package(GLEW) #if (GLEW_FOUND) # include_directories(SYSTEM ${GLEW_INCLUDE_DIRS}) #endif (GLEW_FOUND) # ## BOOST filesystem #set(Boost_USE_STATIC_LIBS OFF) #set(Boost_USE_MULTITHREADED ON) #set(Boost_USE_STATIC_RUNTIME OFF) #set(BOOST_VERSION 1.55.0) #set(BOOST_COMPONENTS filesystem) #find_package(Boost ${BOOST_VERSION} COMPONENTS ${BOOST_COMPONENTS} REQUIRED) #if (Boost_FOUND) # include_directories(SYSTEM ${BOOST_INCLUDE_DIRS}) #endif (Boost_FOUND) hunter_add_package(glew) find_package(glew CONFIG REQUIRED) #hunter_add_package(Boost COMPONENTS filesystem system) #find_package(Boost REQUIRED COMPONENTS filesystem system) hunter_add_package(SDL2) find_package(SDL2 CONFIG REQUIRED) hunter_add_package(minizip) find_package(minizip CONFIG REQUIRED) hunter_add_package(CreateLaunchers) find_package(CreateLaunchers CONFIG REQUIRED) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/../deps/include) #lodepng include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/../deps/lodepng) # readerwriterqueue include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/../deps/readerwriterqueue) #set(extra_sources # ${CMAKE_CURRENT_SOURCE_DIR}/../deps/lodepng/lodepng.cpp # ) #file(GLOB engine_files # "*.h" # "*.cpp" # set(SoA_headers AABBCollidableComponentUpdater.h AmbienceLibrary.h AmbiencePlayer.h AmbienceStream.h Animation.h App.h ARProcessor.h AtmosphereComponentRenderer.h atomicops.h AxisRotationComponentUpdater.h Biome.h BlendState.h BlockData.h BlockLoader.h BlockPack.h BlockTexture.h BlockTextureAtlas.h BlockTextureLoader.h BlockTextureMethods.h BlockTexturePack.h BloomRenderStage.h CAEngine.h Camera.h CellularAutomataTask.h Chunk.h ChunkAccessor.h ChunkAllocator.h ChunkGenerator.h ChunkGrid.h ChunkGridRenderStage.h ChunkHandle.h ChunkID.h ChunkIOManager.h ChunkMesh.h ChunkMesher.h ChunkMeshManager.h ChunkMeshTask.h ChunkQuery.h ChunkRenderer.h ChunkSphereComponentUpdater.h ChunkUpdater.h ClientState.h CloudsComponentRenderer.h Collision.h CollisionComponentUpdater.h ColoredFullQuadRenderer.h ColorFilterRenderStage.h CommonState.h Computer.h ConsoleFuncs.h ConsoleMain.h ConsoleTests.h Constants.h CutoutVoxelRenderStage.h DebugRenderer.h Density.h DevConsole.h DevConsoleView.h DevHudRenderStage.h DevScreen.h DLLAPI.h DLLLoader.h DualContouringMesher.h ECSTemplates.h Errors.h ExposureCalcRenderStage.h FarTerrainComponentRenderer.h FarTerrainComponentUpdater.h FarTerrainPatch.h Flora.h FloraGenerator.h FragFile.h FreeMoveComponentUpdater.h Frustum.h FrustumComponentUpdater.h GameManager.h GameplayLoadScreen.h GameplayRenderer.h GamePlayScreen.h GameRenderParams.h GameSystem.h GameSystemAssemblages.h GameSystemComponentBuilders.h GameSystemComponents.h GameSystemUpdater.h GasGiantComponentRenderer.h GenerateTask.h GeometrySorter.h HdrRenderStage.h HeadComponentUpdater.h ImageAssetLoader.h IniParser.h InitScreen.h InputMapper.h Inputs.h IRenderStage.h Item.h LenseFlareRenderer.h LiquidData.h LiquidVoxelRenderStage.h LoadBar.h LoadContext.h LoadMonitor.h LoadTaskBlockData.h LoadTaskGameManager.h LoadTaskStarSystem.h LoadTaskTextures.h MainMenuLoadScreen.h MainMenuRenderer.h MainMenuScreen.h MainMenuScriptedUI.h MainMenuSystemViewer.h MarchingCubesTable.h MaterialAtlas.h MaterialData.h MaterialStack.h MetaSection.h ModelMesher.h ModInformation.h ModPathResolver.h MTRenderState.h MTRenderStateManager.h MusicPlayer.h NightVisionRenderStage.h Noise.h Octree.h OpaqueVoxelRenderStage.h OptionsController.h OrbitComponentRenderer.h OrbitComponentUpdater.h ParkourComponentUpdater.h ParticleMesh.h PauseMenu.h PauseMenuRenderStage.h PDA.h PdaRenderStage.h PhysicsBlockRenderStage.h PhysicsComponentUpdater.h # Planet.h PlanetGenData.h PlanetGenerator.h PlanetGenLoader.h PlanetHeightData.h # PlanetRenderStage.h PlanetRingsComponentRenderer.h Positional.h ProceduralChunkGenerator.h ProgramGenDelegate.h qef.h readerwriterqueue.h RegionFileManager.h RenderUtils.h ShaderAssetLoader.h ShaderLoader.h SkyboxRenderer.h SkyboxRenderStage.h SoaController.h SoaEngine.h SoaFileSystem.h SoaOptions.h SoAState.h soaUtils.h SonarRenderStage.h SpaceSystem.h SpaceSystemAssemblages.h SpaceSystemComponentBuilders.h SpaceSystemComponents.h SpaceSystemComponentTables.h SpaceSystemLoader.h SpaceSystemLoadStructs.h SpaceSystemRenderStage.h SpaceSystemUpdater.h SphericalHeightmapGenerator.h SphericalTerrainComponentRenderer.h SphericalTerrainComponentUpdater.h SphericalVoxelComponentUpdater.h SsaoRenderStage.h StarComponentRenderer.h Startup.h stdafx.h svd.h SystemARRenderer.h SystemBodyLoader.h TerrainGenTextures.h TerrainPatch.h TerrainPatchConstants.h TerrainPatchMesh.h TerrainPatchMesher.h TerrainPatchMeshManager.h TerrainPatchMeshTask.h TestBiomeScreen.h TestBlockViewScreen.h TestConnectedTextureScreen.h TestConsoleScreen.h TestDeferredScreen.h TestDisplacementMappingScreen.h TestGasGiantScreen.h TestMappingScreen.h TestNewBlockAPIScreen.h TestNoiseScreen.h TestPlanetGenScreen.h TestScriptScreen.h TestStarScreen.h TestUIScreen.h TestVoxelModelScreen.h textureUtils.h Thread.h TransparentVoxelRenderStage.h Vertex.h VoxelBits.h VoxelCoordinateSpaces.h VoxelEditor.h VoxelLightEngine.h VoxelMatrix.h VoxelMesh.h VoxelMesher.h VoxelModel.h VoxelModelLoader.h VoxelModelMesh.h VoxelModelRenderer.h VoxelNodeSetter.h VoxelNodeSetterTask.h VoxelRay.h VoxelSpaceConversions.h VoxelSpaceUtils.h VoxelUpdateBufferer.h VoxelUtils.h VoxelVertices.h VoxPool.h VRayHelper.h # WorldIO.h WorldStructs.h WSO.h WSOAtlas.h WSOData.h WSOScanner.h ZipFile.h ) set(SoA_inline ) set(SoA_sources AABBCollidableComponentUpdater.cpp AmbienceLibrary.cpp AmbiencePlayer.cpp AmbienceStream.cpp App.cpp ARProcessor.cpp AtmosphereComponentRenderer.cpp AxisRotationComponentUpdater.cpp Biome.cpp BlockData.cpp BlockLoader.cpp BlockPack.cpp BlockTexture.cpp BlockTextureLoader.cpp BlockTextureMethods.cpp BlockTexturePack.cpp BloomRenderStage.cpp CAEngine.cpp Camera.cpp CellularAutomataTask.cpp Chunk.cpp ChunkAccessor.cpp ChunkAllocator.cpp ChunkGenerator.cpp ChunkGrid.cpp ChunkGridRenderStage.cpp ChunkIOManager.cpp ChunkMesh.cpp ChunkMesher.cpp ChunkMeshManager.cpp ChunkMeshTask.cpp ChunkQuery.cpp ChunkRenderer.cpp ChunkSphereComponentUpdater.cpp ChunkUpdater.cpp # CloseTerrainPatch.cpp CloudsComponentRenderer.cpp Collision.cpp CollisionComponentUpdater.cpp ColoredFullQuadRenderer.cpp ColorFilterRenderStage.cpp Computer.cpp ConsoleTests.cpp CutoutVoxelRenderStage.cpp DebugRenderer.cpp Density.cpp DevConsole.cpp DevConsoleView.cpp DevHudRenderStage.cpp DevScreen.cpp DualContouringMesher.cpp ECSTemplates.cpp Errors.cpp ExposureCalcRenderStage.cpp FarTerrainComponentRenderer.cpp FarTerrainComponentUpdater.cpp FarTerrainPatch.cpp Flora.cpp FloraGenerator.cpp FragFile.cpp FreeMoveComponentUpdater.cpp Frustum.cpp FrustumComponentUpdater.cpp GameManager.cpp GameplayLoadScreen.cpp GameplayRenderer.cpp GamePlayScreen.cpp GameRenderParams.cpp GameSystem.cpp GameSystemAssemblages.cpp GameSystemComponentBuilders.cpp GameSystemComponents.cpp GameSystemUpdater.cpp GasGiantComponentRenderer.cpp GenerateTask.cpp GeometrySorter.cpp HdrRenderStage.cpp HeadComponentUpdater.cpp ImageAssetLoader.cpp IniParser.cpp InitScreen.cpp InputMapper.cpp Inputs.cpp Item.cpp LenseFlareRenderer.cpp LiquidVoxelRenderStage.cpp LoadBar.cpp LoadMonitor.cpp main.cpp MainMenuLoadScreen.cpp MainMenuRenderer.cpp MainMenuScreen.cpp MainMenuScriptedUI.cpp MainMenuSystemViewer.cpp MetaSection.cpp ModelMesher.cpp ModPathResolver.cpp MTRenderStateManager.cpp MusicPlayer.cpp NightVisionRenderStage.cpp Noise.cpp Octree.cpp OpaqueVoxelRenderStage.cpp OptionsController.cpp OrbitComponentRenderer.cpp OrbitComponentUpdater.cpp ParkourComponentUpdater.cpp PauseMenu.cpp PauseMenuRenderStage.cpp PDA.cpp PdaRenderStage.cpp PhysicsBlockRenderStage.cpp PhysicsComponentUpdater.cpp # Planet.cpp PlanetGenData.cpp PlanetGenerator.cpp PlanetGenLoader.cpp # PlanetRenderStage.cpp PlanetRingsComponentRenderer.cpp ProceduralChunkGenerator.cpp qef.cpp RegionFileManager.cpp ShaderAssetLoader.cpp ShaderLoader.cpp SkyboxRenderer.cpp SkyboxRenderStage.cpp SoaController.cpp SoaEngine.cpp SoaFileSystem.cpp SoaOptions.cpp SoaState.cpp SonarRenderStage.cpp SpaceSystem.cpp SpaceSystemAssemblages.cpp SpaceSystemComponentBuilders.cpp SpaceSystemComponentTables.cpp SpaceSystemLoader.cpp SpaceSystemLoadStructs.cpp SpaceSystemRenderStage.cpp SpaceSystemUpdater.cpp SphericalHeightmapGenerator.cpp SphericalTerrainComponentRenderer.cpp SphericalTerrainComponentUpdater.cpp SphericalVoxelComponentUpdater.cpp SsaoRenderStage.cpp StarComponentRenderer.cpp Startup.cpp stdafx.cpp svd.cpp SystemARRenderer.cpp SystemBodyLoader.cpp TerrainGenTextures.cpp TerrainPatch.cpp TerrainPatchMesh.cpp TerrainPatchMesher.cpp TerrainPatchMeshManager.cpp TerrainPatchMeshTask.cpp TestBiomeScreen.cpp TestBlockViewScreen.cpp TestConnectedTextureScreen.cpp TestConsoleScreen.cpp TestDeferredScreen.cpp TestDisplacementMappingScreen.cpp TestGasGiantScreen.cpp TestMappingScreen.cpp TestNewBlockAPIScreen.cpp TestNoiseScreen.cpp TestPlanetGenScreen.cpp TestScriptScreen.cpp TestStarScreen.cpp TestUIScreen.cpp TestVoxelModelScreen.cpp TransparentVoxelRenderStage.cpp VoxelEditor.cpp VoxelLightEngine.cpp VoxelMatrix.cpp VoxelMesher.cpp VoxelModel.cpp VoxelModelLoader.cpp VoxelModelMesh.cpp VoxelModelRenderer.cpp VoxelNodeSetter.cpp VoxelNodeSetterTask.cpp VoxelRay.cpp VoxelSpaceConversions.cpp VoxelSpaceUtils.cpp VoxPool.cpp VRayHelper.cpp # WorldIO.cpp WorldStructs.cpp WSO.cpp WSOAtlas.cpp WSOScanner.cpp ZipFile.cpp ) set(skip_these_for_now ) #foreach(eng_file in ${skip_these_for_now}) # list(REMOVE_ITEM engine_files ${CMAKE_CURRENT_SOURCE_DIR}/${eng_file}) #endforeach() # doxygen find_package(Doxygen) option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) if(BUILD_DOCUMENTATION) if(NOT DOXYGEN_FOUND) message(FATAL_ERROR "Doxygen is needed to build the documentation.") endif() set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) configure_file(${doxyfile_in} ${doxyfile} @ONLY) add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc) endif() add_executable(soa ${SoA_headers} ${SoA_inline} ${SoA_sources} # ${extra_sources} ) target_link_libraries(soa # ${OPENGL_INCLUDE_DIRS} OpenGL::GL SDL2::SDL2main SDL2::SDL2 glew::glew # Boost::filesystem # Boost::system vorb minizip::minizip # ${SDL_INCLUDE_DIRS} # ${GLEW_INCLUDE_DIRS} # ${BOOST_INCLUDE_DIRS} ) include(CreateLaunchers) create_target_launcher(soa ARGS "-a" RUNTIME_LIBRARY_DIRS "${CMAKE_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../game" ) ================================================ FILE: SoA/Camera.cpp ================================================ #include "stdafx.h" #include "Camera.h" #include #include #include "SoaOptions.h" #include #include const f32v3 Camera::UP_ABSOLUTE = f32v3(0.0f, 1.0f, 0.0f); Camera::Camera() { // Empty } void Camera::init(float aspectRatio) { m_aspectRatio = aspectRatio; } void Camera::offsetPosition(const f64v3& offset) { m_position += offset; m_viewChanged = true; } void Camera::offsetPosition(const f32v3& offset) { m_position += offset; m_viewChanged = true; } void Camera::update() { f32 optFov = soaOptions.get(OPT_FOV).value.f; if (m_fieldOfView != optFov) { setFieldOfView(optFov); } bool updateFrustum = false; if (m_viewChanged) { updateView(); m_viewChanged = false; updateFrustum = true; } if (m_projectionChanged) { updateProjection(); m_projectionChanged = false; updateFrustum = true; } if (updateFrustum) { m_viewProjectionMatrix = m_projectionMatrix * m_viewMatrix; m_frustum.updateFromWVP(m_viewProjectionMatrix); } } void Camera::updateView() { m_viewMatrix = glm::lookAt(f32v3(0.0f), m_direction, m_up); } void Camera::updateProjection() { m_frustum.setCamInternals(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); m_projectionMatrix = glm::perspective(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); } void Camera::applyRotation(const f32q& rot) { m_direction = rot * m_direction; m_right = rot * m_right; m_up = glm::normalize(glm::cross(m_right, m_direction)); m_viewChanged = true; } void Camera::rotateFromMouseAbsoluteUp(float dx, float dy, float speed, bool clampVerticalRotation /* = false*/) { f32q upQuat = glm::angleAxis(dy * speed, m_right); f32q rightQuat = glm::angleAxis(dx * speed, UP_ABSOLUTE); f32v3 previousDirection = m_direction; f32v3 previousUp = m_up; f32v3 previousRight = m_right; applyRotation(upQuat * rightQuat); if (clampVerticalRotation && m_up.y < 0) { m_direction = previousDirection; m_up = previousUp; m_right = previousRight; rotateFromMouseAbsoluteUp(dx, 0.0f, speed); } } void Camera::rotateFromMouse(float dx, float dy, float speed) { f32q upQuat = glm::angleAxis(dy * speed, m_right); f32q rightQuat = glm::angleAxis(dx * speed, m_up); applyRotation(upQuat * rightQuat); } void Camera::rollFromMouse(float dx, float speed) { f32q frontQuat = glm::angleAxis(dx * speed, m_direction); applyRotation(frontQuat); } void Camera::setOrientation(const f64q& orientation) { m_direction = orientation * f64v3(0.0, 0.0, 1.0); m_right = orientation * f64v3(1.0, 0.0, 0.0); m_up = orientation * f64v3(0.0, 1.0, 0.0); m_viewChanged = true; } f32v3 Camera::worldToScreenPoint(const f32v3& worldPoint) const { // Transform world to clipping coordinates f32v4 clipPoint = m_viewProjectionMatrix * f32v4(worldPoint, 1.0f); clipPoint.x /= clipPoint.w; clipPoint.y /= clipPoint.w; clipPoint.z /= clipPoint.w; return f32v3((clipPoint.x + 1.0) / 2.0f, (1.0 - clipPoint.y) / 2.0f, clipPoint.z); } f32v3 Camera::worldToScreenPointLogZ(const f32v3& worldPoint, f32 zFar) const { // Transform world to clipping coordinates f32v4 clipPoint = m_viewProjectionMatrix * f32v4(worldPoint, 1.0f); clipPoint.z = log2(glm::max(0.0001f, clipPoint.w + 1.0f)) * 2.0f / log2(zFar + 1.0f) - 1.0f; clipPoint.x /= clipPoint.w; clipPoint.y /= clipPoint.w; return f32v3((clipPoint.x + 1.0f) / 2.0f, (1.0f - clipPoint.y) / 2.0f, clipPoint.z); } f32v3 Camera::worldToScreenPoint(const f64v3& worldPoint) const { // Transform world to clipping coordinates f64v4 clipPoint = f64m4(m_viewProjectionMatrix) * f64v4(worldPoint, 1.0); clipPoint.x /= clipPoint.w; clipPoint.y /= clipPoint.w; clipPoint.z /= clipPoint.w; return f32v3((clipPoint.x + 1.0f) / 2.0f, (1.0f - clipPoint.y) / 2.0f, clipPoint.z); } f32v3 Camera::worldToScreenPointLogZ(const f64v3& worldPoint, f64 zFar) const { // Transform world to clipping coordinates f64v4 clipPoint = f64m4(m_viewProjectionMatrix) * f64v4(worldPoint, 1.0); clipPoint.z = log2(glm::max(0.0001, clipPoint.w + 1.0)) * 2.0 / log2(zFar + 1.0) - 1.0; clipPoint.x /= clipPoint.w; clipPoint.y /= clipPoint.w; return f32v3((clipPoint.x + 1.0) / 2.0, (1.0 - clipPoint.y) / 2.0, clipPoint.z); } f32v3 Camera::getPickRay(const f32v2& ndcScreenPos) const { f32v4 clipRay(ndcScreenPos.x, ndcScreenPos.y, - 1.0f, 1.0f); f32v4 eyeRay = glm::inverse(m_projectionMatrix) * clipRay; eyeRay = f32v4(eyeRay.x, eyeRay.y, -1.0f, 0.0f); return glm::normalize(f32v3(glm::inverse(m_viewMatrix) * eyeRay)); } void CinematicCamera::update() { m_viewChanged = true; /// Smooth movement towards target if (m_isDynamic) { if (ABS(m_focalLength - m_targetFocalLength) < 0.1) { m_focalLength = m_targetFocalLength; } else { m_focalLength = lerp(m_focalLength, m_targetFocalLength, m_speed); } m_focalPoint = lerp(m_focalPoint, m_targetFocalPoint, m_speed); m_direction = lerp(m_direction, m_targetDirection, (f32)m_speed); m_right = lerp(m_right, m_targetRight, (f32)m_speed); m_position = m_focalPoint - f64v3(m_direction) * m_focalLength; } // Call base class update Camera::update(); } void CinematicCamera::applyRotation(const f32q& rot) { m_targetDirection = rot * m_targetDirection; m_targetRight = rot * m_targetRight; m_viewChanged = true; } void CinematicCamera::rotateFromMouse(float dx, float dy, float speed) { f32q upQuat = glm::angleAxis(dy * speed, m_targetRight); f32v3 targetUp = glm::normalize(glm::cross(m_targetRight, m_targetDirection)); f32q rightQuat = glm::angleAxis(dx * speed, targetUp); applyRotation(upQuat * rightQuat); } void CinematicCamera::rollFromMouse(float dx, float speed) { f32q frontQuat = glm::angleAxis(dx * speed, m_targetDirection); applyRotation(frontQuat); } void CinematicCamera::offsetTargetFocalLength(float offset) { m_targetFocalLength += offset; if (m_targetFocalLength < 0.0) { m_targetFocalLength = 0.0; } else if (m_targetFocalLength > m_maxFocalLength) { m_targetFocalLength = m_maxFocalLength; } } void CinematicCamera::setTarget(const f64v3& targetFocalPoint, const f32v3& targetDirection, const f32v3& targetRight, double targetFocalLength) { m_targetFocalPoint = targetFocalPoint; m_targetDirection = targetDirection; m_targetRight = targetRight; m_targetFocalLength = targetFocalLength; } ================================================ FILE: SoA/Camera.h ================================================ #pragma once #include "Frustum.h" class Camera { public: Camera(); void init(float aspectRatio); void offsetPosition(const f64v3& offset); void offsetPosition(const f32v3& offset); void update(); void updateProjection(); virtual void applyRotation(const f32q& rot); virtual void rotateFromMouseAbsoluteUp(float dx, float dy, float speed, bool clampVerticalRotation = false); virtual void rotateFromMouse(float dx, float dy, float speed); virtual void rollFromMouse(float dx, float speed); // Frustum wrappers bool pointInFrustum(const f32v3& pos) const { return m_frustum.pointInFrustum(pos); } bool sphereInFrustum(const f32v3& pos, float radius) const { return m_frustum.sphereInFrustum(pos, radius); } //setters void setOrientation(const f64q& orientation); void setFocalPoint(const f64v3& focalPoint) { m_focalPoint = focalPoint; m_viewChanged = 1; } void setPosition(const f64v3& position) { m_focalPoint = position; m_position = position; m_focalLength = 0; m_viewChanged = 1; } void setDirection(const f32v3& direction) { m_direction = direction; m_viewChanged = 1; } void setRight(const f32v3& right) { m_right = right; m_viewChanged = 1; } void setUp(const f32v3& up) { m_up = up; m_viewChanged = 1; } void setClippingPlane(float zNear, float zFar){ m_zNear = zNear; m_zFar = zFar; m_projectionChanged = 1; } void setFieldOfView(float fieldOfView){ m_fieldOfView = fieldOfView; m_projectionChanged = 1; } void setFocalLength(float focalLength) { m_focalLength = focalLength; m_viewChanged = 1; } void setAspectRatio(float aspectRatio) { m_aspectRatio = aspectRatio; m_projectionChanged = 1; } // Gets the position of a 3D point on the screen plane f32v3 worldToScreenPoint(const f32v3& worldPoint) const; f32v3 worldToScreenPointLogZ(const f32v3& worldPoint, f32 zFar) const; f32v3 worldToScreenPoint(const f64v3& worldPoint) const; f32v3 worldToScreenPointLogZ(const f64v3& worldPoint, f64 zFar) const; f32v3 getPickRay(const f32v2& ndcScreenPos) const; //getters const f64v3& getPosition() const { return m_position; } const f32v3& getDirection() const { return m_direction; } const f32v3& getRight() const { return m_right; } const f32v3& getUp() const { return m_up; } const f32m4& getProjectionMatrix() const { return m_projectionMatrix; } const f32m4& getViewMatrix() const { return m_viewMatrix; } const f32m4& getViewProjectionMatrix() const { return m_viewProjectionMatrix; } const f32& getNearClip() const { return m_zNear; } const f32& getFarClip() const { return m_zFar; } const f32& getFieldOfView() const { return m_fieldOfView; } const f32& getAspectRatio() const { return m_aspectRatio; } const f64& getFocalLength() const { return m_focalLength; } const Frustum& getFrustum() const { return m_frustum; } protected: void normalizeAngles(); void updateView(); f32 m_zNear = 0.1f; f32 m_zFar = 100000.0f; f32 m_fieldOfView = 75.0f; f32 m_aspectRatio = 4.0f / 3.0f; f64 m_focalLength = 0.0; f64 m_maxFocalLength = 10000000000000000000000.0; bool m_viewChanged = true; bool m_projectionChanged = true; f64v3 m_focalPoint = f64v3(0.0); f64v3 m_position = f64v3(0.0); f32v3 m_direction = f32v3(1.0f, 0.0f, 0.0f); f32v3 m_right = f32v3(0.0f, 0.0f, 1.0f); f32v3 m_up = f32v3(0.0f, 1.0f, 0.0f); static const f32v3 UP_ABSOLUTE; f32m4 m_projectionMatrix; f32m4 m_viewMatrix; f32m4 m_viewProjectionMatrix; Frustum m_frustum; ///< For frustum culling }; class CinematicCamera : public Camera { public: void update(); virtual void applyRotation(const f32q& rot) override; virtual void rotateFromMouse(float dx, float dy, float speed) override; virtual void rollFromMouse(float dx, float speed) override; void offsetTargetFocalLength(float offset); // Getters const f64& getTargetFocalLength() const { return m_targetFocalLength; } const f64& getSpeed() const { return m_speed; } // Setters void setIsDynamic(bool isDynamic) { m_isDynamic = isDynamic; } void setSpeed(f64 speed) { m_speed = speed; } void setTarget(const f64v3& targetFocalPoint, const f32v3& targetDirection, const f32v3& targetRight, f64 targetFocalLength); void setTargetDirection(const f32v3& targetDirection) { m_targetDirection = targetDirection; } void setTargetRight(const f32v3& targetRight) { m_targetRight = targetRight; } void setTargetFocalPoint(const f64v3& targetFocalPoint) { m_targetFocalPoint = targetFocalPoint; } void setTargetFocalLength(const float& targetFocalLength) { m_targetFocalLength = targetFocalLength; } private: bool m_isDynamic = true; f32v3 m_targetDirection = m_direction; ///< Desired direction f32v3 m_targetRight = m_right; ///< Desired right f64v3 m_targetFocalPoint = m_focalPoint; ///< Target focal position f64 m_targetFocalLength = m_focalLength; ///< Desired focal length f64 m_speed = 0.3; ///< The speed of the camera. 1.0 is the highest }; ================================================ FILE: SoA/CellularAutomataTask.cpp ================================================ #include "stdafx.h" #include "CellularAutomataTask.h" #include "CAEngine.h" #include "Chunk.h" #include "ChunkMeshManager.h" #include "VoxPool.h" CellularAutomataTask::CellularAutomataTask(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, OPT ChunkMeshManager* meshManager VORB_UNUSED) : IThreadPoolTask(CA_TASK_ID), _chunk(chunk), m_chunkManager(chunkManager), m_physicsEngine(physicsEngine) { //return; //typesToUpdate.reserve(2); //if (meshManager) { // chunk->queuedForMesh = true; // renderTask = new RenderTask(); // renderTask->init(_chunk, RenderTaskType::DEFAULT, meshManager); //} } void CellularAutomataTask::execute(WorkerData* workerData VORB_UNUSED) { // if (workerData->caEngine == nullptr) { // workerData->caEngine = new CAEngine(m_chunkManager, m_physicsEngine); // } // workerData->caEngine->setChunk(_chunk); // for (auto& type : typesToUpdate) { // switch (type->getCaAlg()) { // case CA_ALGORITHM::LIQUID: //// workerData->caEngine->updateLiquidBlocks(type->getCaIndex()); // break; // case CA_ALGORITHM::POWDER: //// workerData->caEngine->updatePowderBlocks(type->getCaIndex()); // break; // default: // break; // } // } // if (renderTask) { // renderTask->execute(workerData); // } //// updateSpawnerBlocks(frameCounter == 0); } ================================================ FILE: SoA/CellularAutomataTask.h ================================================ /// /// CellularAutomataTask.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 24 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Implements the celluar automata task for multithreaded physics /// #pragma once #ifndef CellularAutomataTask_h__ #define CellularAutomataTask_h__ #include #include "VoxPool.h" class CaPhysicsType; class Chunk; class ChunkManager; class ChunkMeshManager; class PhysicsEngine; class ChunkMeshTask; #define CA_TASK_ID 3 enum class CAAlgorithm { NONE = 0, LIQUID = 1, POWDER = 2 }; class CellularAutomataTask : public vcore::IThreadPoolTask { public: friend class ChunkManager; friend class SphericalVoxelComponentUpdater; /// Constructs the task CellularAutomataTask(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, OPT ChunkMeshManager* meshManager); /// Adds a caPhysicsType for update void addCaTypeToUpdate(CaPhysicsType* caType) { typesToUpdate.push_back(caType); } /// Executes the task void execute(WorkerData* workerData) override; ChunkMeshTask* renderTask = nullptr; ///< A nested to force re-mesh private: std::vector typesToUpdate; ///< All the CA types that will be updated by this task Chunk* _chunk = nullptr; ///< The chunk we are updating ChunkManager* m_chunkManager = nullptr; PhysicsEngine* m_physicsEngine = nullptr; ChunkMeshManager* meshManager = nullptr; }; #endif // CellularAutomataTask_h__ ================================================ FILE: SoA/Chunk.cpp ================================================ #include "stdafx.h" #include "Chunk.h" #include "VoxelSpaceConversions.h" Event Chunk::DataChange; void Chunk::init(WorldCubeFace face) { // Get position from ID m_chunkPosition.pos = i32v3(m_id.x, m_id.y, m_id.z); m_chunkPosition.face = face; m_voxelPosition = VoxelSpaceConversions::chunkToVoxel(m_chunkPosition); } void Chunk::initAndFillEmpty(WorldCubeFace face, vvox::VoxelStorageState /*= vvox::VoxelStorageState::INTERVAL_TREE*/) { init(face); IntervalTree::LNode blockNode; IntervalTree::LNode tertiaryNode; blockNode.set(0, CHUNK_SIZE, 0); tertiaryNode.set(0, CHUNK_SIZE, 0); blocks.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, &blockNode, 1); tertiary.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, &tertiaryNode, 1); } void Chunk::setRecyclers(vcore::FixedSizeArrayRecycler* shortRecycler) { blocks.setArrayRecycler(shortRecycler); tertiary.setArrayRecycler(shortRecycler); } void Chunk::updateContainers() { blocks.update(dataMutex); tertiary.update(dataMutex); } ================================================ FILE: SoA/Chunk.h ================================================ // // NChunk.h // Seed of Andromeda // // Created by Cristian Zaloj on 25 May 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef NChunk_h__ #define NChunk_h__ #include "Constants.h" #include "SmartVoxelContainer.hpp" #include "VoxelCoordinateSpaces.h" #include "PlanetHeightData.h" #include "MetaSection.h" #include "ChunkGenerator.h" #include "ChunkID.h" #include #if defined(_MSC_VER) #define ALIGNED_(x) __declspec(align(x)) #else #define ALIGNED_(x) __attribute__ ((aligned(x))) #endif class Chunk; typedef Chunk* ChunkPtr; // TODO(Ben): Move to file typedef ui16 BlockIndex; class ChunkGridData { public: ChunkGridData():isLoading(false), isLoaded(false), refCount(1){}; ChunkGridData(const ChunkPosition3D& pos):isLoading(false), isLoaded(false), refCount(1) { gridPosition.pos = i32v2(pos.pos.x, pos.pos.z); gridPosition.face = pos.face; } ChunkPosition2D gridPosition; PlanetHeightData heightData[CHUNK_LAYER]; bool isLoading; bool isLoaded; int refCount; }; // TODO(Ben): Can lock two chunks without deadlock worry with checkerboard pattern updates. class Chunk { friend class ChunkAccessor; friend class ChunkGenerator; friend class ChunkGrid; friend class ChunkMeshManager; friend class ChunkMeshTask; friend class PagedChunkAllocator; friend class SphericalVoxelComponentUpdater; public: Chunk() : neighbor(), genLevel(ChunkGenLevel::GEN_NONE), pendingGenLevel(ChunkGenLevel::GEN_NONE), isAccessible(false), accessor(nullptr), m_inLoadRange(false), m_handleState(0), m_handleRefCount(0) {} // Initializes the chunk but does not set voxel data // Should be called after ChunkAccessor sets m_id void init(WorldCubeFace face); // Initializes the chunk and sets all voxel data to 0 void initAndFillEmpty(WorldCubeFace face, vvox::VoxelStorageState = vvox::VoxelStorageState::INTERVAL_TREE); void setRecyclers(vcore::FixedSizeArrayRecycler* shortRecycler); void updateContainers(); /************************************************************************/ /* Getters */ /************************************************************************/ const ChunkPosition3D& getChunkPosition() const { return m_chunkPosition; } const VoxelPosition3D& getVoxelPosition() const { return m_voxelPosition; } const ChunkID& getID() const { return m_id; } inline ui16 getBlockData(int c) const { return blocks.get(c); } inline ui16 getTertiaryData(int c) const { return tertiary.get(c); } void setBlock(int x, int y, int z, ui16 id) { blocks.set(x + y * CHUNK_LAYER + z * CHUNK_WIDTH, id); } // Marks the chunks as dirty and flags for a re-mesh void flagDirty() { isDirty = true; } /************************************************************************/ /* Members */ /************************************************************************/ // Everything else uses this grid data handle ChunkGridData* gridData = nullptr; MetaFieldInformation meta; union { struct { ChunkHandle left; ChunkHandle right; ChunkHandle bottom; ChunkHandle top; ChunkHandle back; ChunkHandle front; } neighbor; ChunkHandle neighbors[6]; }; volatile ChunkGenLevel genLevel; ChunkGenLevel pendingGenLevel; bool isDirty; f32 distance2; //< Squared distance int numBlocks; // TODO(Ben): reader/writer lock std::mutex dataMutex; volatile bool isAccessible; // TODO(Ben): Think about data locality. vvox::SmartVoxelContainer blocks; vvox::SmartVoxelContainer tertiary; // Block indexes where flora must be generated. std::vector floraToGenerate; volatile ui32 updateVersion; ChunkAccessor* accessor; static Event DataChange; private: // For generation ChunkGenQueryData m_genQueryData; // ui32 m_loadingNeighbors = 0u; ///< Seems like a good idea to get rid of isAccesible ChunkPosition3D m_chunkPosition; VoxelPosition3D m_voxelPosition; ui32 m_activeIndex; ///< Position in active list for m_chunkGrid bool m_inLoadRange; ChunkID m_id; /************************************************************************/ /* Chunk Handle Data */ /************************************************************************/ std::mutex m_handleMutex; ALIGNED_(4) volatile ui32 m_handleState; ALIGNED_(4) volatile ui32 m_handleRefCount; }; #endif // NChunk_h__ ================================================ FILE: SoA/ChunkAccessor.cpp ================================================ #include "stdafx.h" #include "ChunkAccessor.h" #include "ChunkAllocator.h" const ui32 HANDLE_STATE_ALIVE = 2; #ifdef FAST_CHUNK_ACCESS const ui32 HANDLE_STATE_DEAD = 0; const ui32 HANDLE_STATE_ACQUIRING = 1; const ui32 HANDLE_STATE_FREEING = 3; #endif ChunkHandle::ChunkHandle(const ChunkHandle& other) : m_accessor(other.m_acquired ? other.m_chunk->accessor : other.m_accessor), m_id(other.m_id), m_acquired(false) { // Empty } ChunkHandle& ChunkHandle::operator= (const ChunkHandle& other) { m_acquired = false; m_accessor = other.m_acquired ? other.m_chunk->accessor : other.m_accessor; m_id = other.m_id; return *this; } void ChunkHandle::acquireSelf() { if (!m_acquired) m_chunk->accessor->acquire(*this); } ChunkHandle ChunkHandle::acquire() { if (m_acquired) { ChunkHandle h(m_chunk->accessor->acquire(*this)); h.m_acquired = true; h.m_chunk = m_chunk; return h; } else { return m_accessor->acquire(m_id); } } void ChunkHandle::release() { if (m_acquired) m_chunk->accessor->release(*this); } void ChunkAccessor::init(PagedChunkAllocator* allocator) { m_allocator = allocator; } void ChunkAccessor::destroy() { std::unordered_map().swap(m_chunkLookup); } #ifdef FAST_CHUNK_ACCESS ChunkHandle ChunkAccessor::acquire(ChunkID id) { std::unique_lock lMap(m_lckLookup); auto& it = m_chunkLookup.find(id); if (it == m_chunkLookup.end()) { ChunkHandle& h = m_chunkLookup[id]; h.m_chunk = m_allocator->alloc(); h->m_handleRefCount = 1; h->m_id = id; h->accessor = this; h->m_handleState = HANDLE_STATE_ALIVE; lMap.unlock(); onAdd(ChunkHandle(h)); return h; } else { InterlockedIncrement(&it->second->m_handleRefCount); it->second->m_handleState = HANDLE_STATE_ALIVE; return it->second; } } ChunkHandle ChunkAccessor::acquire(ChunkHandle& chunk) { switch (InterlockedCompareExchange(&chunk->m_handleState, HANDLE_STATE_ACQUIRING, HANDLE_STATE_ALIVE)) { case HANDLE_STATE_FREEING: return acquire(chunk->m_id); case HANDLE_STATE_ACQUIRING: InterlockedIncrement(&chunk->m_handleRefCount); return chunk; case HANDLE_STATE_ALIVE: InterlockedIncrement(&chunk->m_handleRefCount); InterlockedCompareExchange(&chunk->m_handleState, HANDLE_STATE_ALIVE, HANDLE_STATE_ACQUIRING); return chunk; default: throw std::invalid_argument("INVALID CHUNK HANDLE STATE"); } } void ChunkAccessor::release(ChunkHandle& chunk) { ui32 retries = 0; // TODO(Cristian): Heavy thread-safety ui32 currentCount = InterlockedDecrement(&chunk->m_handleRefCount); RETEST_CHUNK_LIVELINESS: if (currentCount == 0) { // If the chunk is alive, set it to zombie mode. Otherwise, it's being birthed or already dead. switch (InterlockedCompareExchange(&chunk->m_handleState, HANDLE_STATE_FREEING, HANDLE_STATE_ALIVE)) { case HANDLE_STATE_ALIVE: { // Highlander... there can only be one... killer of chunks std::lock_guard chunkLock(chunk->m_handleMutex); currentCount = InterlockedDecrement(&chunk->m_handleRefCount); if (chunk->m_handleState == HANDLE_STATE_FREEING) { // We are able to kill this chunk safeRemove(chunk); } } case HANDLE_STATE_FREEING: // Let the other thread do the work break; case HANDLE_STATE_ACQUIRING: // Need to retry std::this_thread::yield(); currentCount = chunk->m_handleRefCount; retries++; goto RETEST_CHUNK_LIVELINESS; } } // Invalidate the handle chunk.m_chunk = nullptr; if (retries > 2) { printf("A lot of release retries occurred: %d\n", retries); fflush(stdout); } } #else ChunkHandle ChunkAccessor::acquire(ChunkID id) { bool wasOld; ChunkHandle chunk = safeAdd(id, wasOld); if (wasOld) return acquire(chunk); chunk.m_acquired = true; return chunk; } ChunkHandle ChunkAccessor::acquire(ChunkHandle& chunk) { std::lock_guard lChunk(chunk->m_handleMutex); if (chunk->m_handleRefCount == 0) { // We need to re-add the chunk bool wasOld = false; chunk.m_acquired = true; return safeAdd(chunk.m_id, wasOld); } else { ChunkHandle retValue = {}; memcpy(&retValue, &chunk, sizeof(ChunkHandle)); retValue->m_handleRefCount++; retValue.m_acquired = true; return retValue; } } void ChunkAccessor::release(ChunkHandle& chunk) { std::lock_guard lChunk(chunk->m_handleMutex); chunk->m_handleRefCount--; if (chunk->m_handleRefCount == 0) { // We need to remove the chunk safeRemove(chunk); } chunk.m_acquired = false; chunk.m_accessor = this; } #endif ChunkHandle ChunkAccessor::safeAdd(ChunkID id, bool& wasOld) { std::unique_lock l(m_lckLookup); auto it = m_chunkLookup.find(id); if (it == m_chunkLookup.end()) { wasOld = false; ChunkHandle& h = m_chunkLookup[id]; h.m_chunk = m_allocator->alloc(); h.m_id = id; h->m_id = id; h->accessor = this; h->m_handleState = HANDLE_STATE_ALIVE; h->m_handleRefCount = 1; ChunkHandle tmp(h); l.unlock(); onAdd(tmp); return h; } else { wasOld = true; return it->second; } } void ChunkAccessor::safeRemove(ChunkHandle& chunk) { { // TODO(Cristian): This needs to be added to a free-list? std::lock_guard l(m_lckLookup); // Make sure it can't be accessed until acquired again chunk->accessor = nullptr; // TODO(Ben): Time based free? m_chunkLookup.erase(chunk.m_id); } // Fire event before deallocating onRemove(chunk); m_allocator->free(chunk.m_chunk); } ================================================ FILE: SoA/ChunkAccessor.h ================================================ // // ChunkAccessor.h // Seed of Andromeda // // Created by Benjamin Arnold on 30 Jul 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Fires events for chunk access // #pragma once #ifndef ChunkAccessor_h__ #define ChunkAccessor_h__ #include "Chunk.h" #include "ChunkHandle.h" #include class ChunkAccessor { friend class ChunkHandle; public: void init(PagedChunkAllocator* allocator); void destroy(); ChunkHandle acquire(ChunkID id); size_t getCountAlive() const { return m_chunkLookup.size(); } Event onAdd; ///< Called when a handle is added Event onRemove; ///< Called when a handle is removed private: ChunkHandle acquire(ChunkHandle& chunk); void release(ChunkHandle& chunk); ChunkHandle safeAdd(ChunkID id, bool& wasOld); void safeRemove(ChunkHandle& chunk); std::mutex m_lckLookup; std::unordered_map m_chunkLookup; PagedChunkAllocator* m_allocator = nullptr; }; #endif // ChunkAccessor_h__ ================================================ FILE: SoA/ChunkAllocator.cpp ================================================ #include "stdafx.h" #include "ChunkAllocator.h" #include "Chunk.h" #define MAX_VOXEL_ARRAYS_TO_CACHE 200 #define NUM_SHORT_VOXEL_ARRAYS 3 #define NUM_BYTE_VOXEL_ARRAYS 1 #define INITIAL_UPDATE_VERSION 1 PagedChunkAllocator::PagedChunkAllocator() : m_shortFixedSizeArrayRecycler(MAX_VOXEL_ARRAYS_TO_CACHE * NUM_SHORT_VOXEL_ARRAYS) { // Empty } PagedChunkAllocator::~PagedChunkAllocator() { for (auto& page : m_chunkPages) { delete page; } } Chunk* PagedChunkAllocator::alloc() { // TODO(Ben): limit std::lock_guard lock(m_lock); // Allocate chunk pages if needed if (m_freeChunks.empty()) { ChunkPage* page = new ChunkPage(); m_chunkPages.push_back(page); // Add chunks to free chunks lists for (size_t i = 0; i < CHUNK_PAGE_SIZE; i++) { Chunk* chunk = &page->chunks[CHUNK_PAGE_SIZE - i - 1]; chunk->setRecyclers(&m_shortFixedSizeArrayRecycler); m_freeChunks.push_back(chunk); } } // Grab a free chunk Chunk* chunk = m_freeChunks.back(); m_freeChunks.pop_back(); // Set defaults chunk->gridData = nullptr; chunk->m_inLoadRange = false; chunk->numBlocks = 0; chunk->genLevel = ChunkGenLevel::GEN_NONE; chunk->pendingGenLevel = ChunkGenLevel::GEN_NONE; chunk->isAccessible = false; chunk->distance2 = FLT_MAX; chunk->updateVersion = INITIAL_UPDATE_VERSION; memset(chunk->neighbors, 0, sizeof(chunk->neighbors)); chunk->m_genQueryData.current = nullptr; return chunk; } void PagedChunkAllocator::free(Chunk* chunk) { // TODO(Ben): Deletion if there is a lot? std::lock_guard lock(m_lock); m_freeChunks.push_back(chunk); // Free data chunk->blocks.clear(); chunk->tertiary.clear(); std::vector().swap(chunk->m_genQueryData.pending); } ================================================ FILE: SoA/ChunkAllocator.h ================================================ // // ChunkAllocator.h // Seed of Andromeda // // Created by Benjamin Arnold on 9 Jun 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Paged memory management system for chunks. // #pragma once #ifndef ChunkAllocator_h__ #define ChunkAllocator_h__ #include #include "Chunk.h" #include "Constants.h" /*! @brief The chunk allocator. */ class PagedChunkAllocator { friend class SphericalVoxelComponentUpdater; public: PagedChunkAllocator(); ~PagedChunkAllocator(); typedef void(*MemoryFormatter)(Chunk* chunk, void* memory, void* userData); //void appendExtraSize(size_t s, MemoryFormatter fConstructor); /// Gets a new chunk ID Chunk* alloc(); /// Frees a chunk void free(Chunk* chunk); protected: static const size_t CHUNK_PAGE_SIZE = 2048; struct ChunkPage { Chunk chunks[CHUNK_PAGE_SIZE]; }; std::vector m_freeChunks; ///< List of inactive chunks std::vector m_chunkPages; ///< All pages vcore::FixedSizeArrayRecycler m_shortFixedSizeArrayRecycler; ///< For recycling voxel data std::mutex m_lock; ///< Lock access to free-list }; #endif // ChunkAllocator_h__ ================================================ FILE: SoA/ChunkGenerator.cpp ================================================ #include "stdafx.h" #include "ChunkGenerator.h" #include "ChunkAllocator.h" #include "Chunk.h" #include "ChunkHandle.h" #include "ChunkGrid.h" void ChunkGenerator::init(vcore::ThreadPool* threadPool, PlanetGenData* genData, ChunkGrid* grid) { m_threadPool = threadPool; m_proceduralGenerator.init(genData); m_grid = grid; } void ChunkGenerator::submitQuery(ChunkQuery* query) { Chunk& chunk = query->chunk; // Check if its already done if (chunk.genLevel >= query->genLevel) { query->m_isFinished = true; query->m_cond.notify_all(); query->chunk.release(); if (query->shouldRelease) query->release(); return; } if (chunk.pendingGenLevel < query->genLevel) { chunk.pendingGenLevel = query->genLevel; } if (!chunk.gridData->isLoaded) { // If this heightmap isn't already loading, send it if (!chunk.gridData->isLoading) { // Send heightmap gen query chunk.gridData->isLoading = true; m_threadPool->addTask(&query->genTask); } // Store as a pending query m_pendingQueries[chunk.gridData].push_back(query); } else { if (chunk.m_genQueryData.current) { // Only one gen query should be active at a time so just store this one chunk.m_genQueryData.pending.push_back(query); } else { // Submit for generation chunk.m_genQueryData.current = query; m_threadPool->addTask(&query->genTask); } } } void ChunkGenerator::finishQuery(ChunkQuery* query) { m_finishedQueries.enqueue(query); } // Updates finished queries void ChunkGenerator::update() { #define MAX_QUERIES 100 ChunkQuery* queries[MAX_QUERIES]; size_t numQueries = m_finishedQueries.try_dequeue_bulk(queries, MAX_QUERIES); for (size_t i = 0; i < numQueries; i++) { ChunkQuery* q = queries[i]; Chunk& chunk = q->chunk; chunk.m_genQueryData.current = nullptr; // Check if it was a heightmap gen if (chunk.gridData->isLoading) { chunk.gridData->isLoaded = true; chunk.gridData->isLoading = false; // Submit all the pending queries on this grid data auto it = m_pendingQueries.find(chunk.gridData); // TODO(Ben): Should this be shared? ( I don't think it should ) for (auto& p : it->second) { submitQuery(p); } m_pendingQueries.erase(it); } else if (chunk.genLevel == GEN_DONE) { // If the chunk is done generating, we can signal all queries as done. for (auto& q2 : chunk.m_genQueryData.pending) { q2->m_isFinished = true; q2->m_cond.notify_all(); chunk.isAccessible = true; q2->chunk.release(); if (q2->shouldRelease) q2->release(); } std::vector().swap(chunk.m_genQueryData.pending); // Notify listeners that this chunk is finished onGenFinish(q->chunk, q->genLevel); q->chunk.release(); if (q->shouldRelease) q->release(); } else { // Otherwise possibly only some queries are done for (size_t i = 0; i < chunk.m_genQueryData.pending.size();) { auto q2 = chunk.m_genQueryData.pending[i]; if (q2->genLevel <= chunk.genLevel) { // TODO(Ben): This looks wrong. We don't remove from pending. q2->m_isFinished = true; q2->m_cond.notify_all(); chunk.isAccessible = true; // TODO(Ben): Do we care about order? chunk.m_genQueryData.pending[i] = chunk.m_genQueryData.pending.back(); chunk.m_genQueryData.pending.pop_back(); q2->chunk.release(); if (q2->shouldRelease) q2->release(); } else { i++; } } // Submit a pending query if (chunk.m_genQueryData.pending.size()) { // Submit for generation q = chunk.m_genQueryData.pending.back(); chunk.m_genQueryData.pending.pop_back(); chunk.m_genQueryData.current = q; m_threadPool->addTask(&q->genTask); } // Notify listeners that this chunk is finished onGenFinish(q->chunk, q->genLevel); q->chunk.release(); if (q->shouldRelease) q->release(); } } } ================================================ FILE: SoA/ChunkGenerator.h ================================================ /// /// ChunkGenerator.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 10 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// /// #pragma once #ifndef ChunkGenerator_h__ #define ChunkGenerator_h__ #include #include "VoxPool.h" #include "ProceduralChunkGenerator.h" #include "PlanetGenData.h" #include "GenerateTask.h" #include "ChunkQuery.h" class PagedChunkAllocator; class ChunkGridData; class ChunkGrid; // Data stored in Chunk and used only by ChunkGenerator struct ChunkGenQueryData { friend class ChunkGenerator; friend class ChunkGrid; friend class PagedChunkAllocator; private: ChunkQuery* current = nullptr; std::vector pending; }; class ChunkGenerator { friend class GenerateTask; public: void init(vcore::ThreadPool* threadPool, PlanetGenData* genData, ChunkGrid* grid); void submitQuery(ChunkQuery* query); void finishQuery(ChunkQuery* query); // Updates finished queries void update(); Event onGenFinish; private: void tryFlagMeshableNeighbors(ChunkHandle& ch); void flagMeshbleNeighbor(ChunkHandle& n, ui32 bit); moodycamel::ConcurrentQueue m_finishedQueries; std::map < ChunkGridData*, std::vector >m_pendingQueries; ///< Queries waiting on height map ChunkGrid* m_grid = nullptr; ProceduralChunkGenerator m_proceduralGenerator; vcore::ThreadPool* m_threadPool = nullptr; }; #endif // ChunkGenerator_h__ ================================================ FILE: SoA/ChunkGrid.cpp ================================================ #include "stdafx.h" #include "ChunkGrid.h" #include "Chunk.h" #include "ChunkAllocator.h" #include "soaUtils.h" #include void ChunkGrid::init(WorldCubeFace face, OPT vcore::ThreadPool* threadPool, ui32 generatorsPerRow, PlanetGenData* genData, PagedChunkAllocator* allocator) { m_face = face; this->generatorsPerRow = generatorsPerRow; numGenerators = generatorsPerRow * generatorsPerRow; generators = new ChunkGenerator[numGenerators]; for (ui32 i = 0; i < numGenerators; i++) { generators[i].init(threadPool, genData, this); } accessor.init(allocator); accessor.onAdd += makeDelegate(this, &ChunkGrid::onAccessorAdd); accessor.onRemove += makeDelegate(this, &ChunkGrid::onAccessorRemove); nodeSetter.grid = this; nodeSetter.threadPool = threadPool; } void ChunkGrid::dispose() { accessor.onAdd -= makeDelegate(this, &ChunkGrid::onAccessorAdd); accessor.onRemove -= makeDelegate(this, &ChunkGrid::onAccessorRemove); delete[] generators; generators = nullptr; } ChunkQuery* ChunkGrid::submitQuery(const i32v3& chunkPos, ChunkGenLevel genLevel, bool shouldRelease) { ChunkQuery* query; { std::lock_guard l(m_lckQueryRecycler); query = m_queryRecycler.create(); } query->chunkPos = chunkPos; query->genLevel = genLevel; query->shouldRelease = shouldRelease; query->grid = this; query->m_isFinished = false; ChunkID id(query->chunkPos); query->chunk = accessor.acquire(id); m_queries.enqueue(query); // TODO(Ben): RACE CONDITION HERE: There is actually a very small chance that // chunk will get freed before the callee can acquire the chunk, if this runs // on another thread. return query; } void ChunkGrid::releaseQuery(ChunkQuery* query) { assert(query->grid); query->grid = nullptr; { std::lock_guard l(m_lckQueryRecycler); m_queryRecycler.recycle(query); } } ChunkGridData* ChunkGrid::getChunkGridData(const i32v2& gridPos) { std::lock_guard l(m_lckGridData); auto it = m_chunkGridDataMap.find(gridPos); if (it == m_chunkGridDataMap.end()) return nullptr; return it->second; } void ChunkGrid::update() { // TODO(Ben): Handle generator distribution generators[0].update(); /* Update Queries */ // Needs to be big so we can flush it every frame. #define MAX_QUERIES 5000 ChunkQuery* queries[MAX_QUERIES]; size_t numQueries = m_queries.try_dequeue_bulk(queries, MAX_QUERIES); for (size_t i = 0; i < numQueries; i++) { ChunkQuery* q = queries[i]; // TODO(Ben): Handle generator distribution q->genTask.init(q, q->chunk->gridData->heightData, &generators[0]); generators[0].submitQuery(q); } // Place any needed nodes nodeSetter.update(); } void ChunkGrid::onAccessorAdd(Sender s VORB_MAYBE_UNUSED, ChunkHandle& chunk) { { // Add to active list std::lock_guard l(m_lckActiveChunks); chunk->m_activeIndex = m_activeChunks.size(); m_activeChunks.push_back(chunk); } // Init the chunk chunk->init(m_face); i32v2 gridPos = chunk->getChunkPosition(); { // Get grid data std::lock_guard l(m_lckGridData); // Check and see if the grid data is already allocated here auto it = m_chunkGridDataMap.find(gridPos); if (it == m_chunkGridDataMap.end()) { // If its not allocated, make a new one with a new voxelMapData // TODO(Ben): Cache this chunk->gridData = new ChunkGridData(chunk->getChunkPosition()); m_chunkGridDataMap[gridPos] = chunk->gridData; } else { chunk->gridData = it->second; chunk->gridData->refCount++; } } } void ChunkGrid::onAccessorRemove(Sender s VORB_MAYBE_UNUSED, ChunkHandle& chunk) { { // Remove from active list std::lock_guard l(m_lckActiveChunks); m_activeChunks[chunk->m_activeIndex] = m_activeChunks.back(); m_activeChunks[chunk->m_activeIndex]->m_activeIndex = chunk->m_activeIndex; m_activeChunks.pop_back(); } // TODO(Ben): Could be slightly faster with InterlockedDecrement and FSM? { // Remove and possibly free grid data std::unique_lock l(m_lckGridData); chunk->gridData->refCount--; if (chunk->gridData->refCount == 0) { m_chunkGridDataMap.erase(chunk->getChunkPosition()); l.unlock(); delete chunk->gridData; chunk->gridData = nullptr; } } } ================================================ FILE: SoA/ChunkGrid.h ================================================ /// /// ChunkGrid.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 26 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Grid of chunks for a voxel world /// #pragma once #ifndef ChunkGrid_h__ #define ChunkGrid_h__ #include #include #include #include "VoxelCoordinateSpaces.h" #include "ChunkGenerator.h" #include "Chunk.h" #include "ChunkAllocator.h" #include "ChunkAccessor.h" #include "ChunkHandle.h" #include "VoxelNodeSetter.h" class BlockPack; class ChunkGrid { friend class ChunkMeshManager; public: void init(WorldCubeFace face, OPT vcore::ThreadPool* threadPool, ui32 generatorsPerRow, PlanetGenData* genData, PagedChunkAllocator* allocator); void dispose(); /// Will generate chunk if it doesn't exist /// @param gridPos: The position of the chunk to get. /// @param genLevel: The required generation level. /// @param shouldRelease: Will automatically release when true. ChunkQuery* submitQuery(const i32v3& chunkPos, ChunkGenLevel genLevel, bool shouldRelease); /// Releases and recycles a query. void releaseQuery(ChunkQuery* query); /// Gets a chunkGridData for a specific 2D position /// @param gridPos: The grid position for the data ChunkGridData* getChunkGridData(const i32v2& gridPos); // Processes chunk queries and set active chunks void update(); // Locks and gets active chunks. Must call releaseActiveChunks() later. const std::vector& acquireActiveChunks() { m_lckActiveChunks.lock(); return m_activeChunks; } void releaseActiveChunks() { m_lckActiveChunks.unlock(); } ChunkGenerator* generators = nullptr; ui32 generatorsPerRow; ui32 numGenerators; ChunkAccessor accessor; BlockPack* blockPack = nullptr; ///< Handle to the block pack for this grid VoxelNodeSetter nodeSetter; Event onNeighborsAcquire; Event onNeighborsRelease; private: /************************************************************************/ /* Event Handlers */ /************************************************************************/ void onAccessorAdd(Sender s, ChunkHandle& chunk); void onAccessorRemove(Sender s, ChunkHandle& chunk); moodycamel::ConcurrentQueue m_queries; std::mutex m_lckActiveChunks; std::vector m_activeChunks; // TODO(Ben): Compare to std::map performance std::mutex m_lckGridData; std::unordered_map m_chunkGridDataMap; ///< 2D grid specific data vcore::IDGenerator m_idGenerator; std::mutex m_lckQueryRecycler; PtrRecycler m_queryRecycler; WorldCubeFace m_face = FACE_NONE; }; #endif // ChunkGrid_h__ ================================================ FILE: SoA/ChunkGridRenderStage.cpp ================================================ #include "stdafx.h" #include "ChunkGridRenderStage.h" #include #include "Camera.h" #include "Chunk.h" #include "Frustum.h" #include "GameRenderParams.h" #include "ShaderLoader.h" #include "soaUtils.h" #include "ChunkGrid.h" namespace { // Default shader source const cString VERT_SRC = R"( uniform mat4 MVP; in vec3 vPosition; in vec4 vTint; in vec2 vUV; out vec4 fTint; void main() { fTint = vTint; gl_Position = MVP * vec4(vPosition, 1.0); } )"; const cString FRAG_SRC = R"( uniform float unZCoef; in vec4 fTint; out vec4 fColor; void main() { fColor = fTint; } )"; } void ChunkGridRenderStage::hook(const GameRenderParams* gameRenderParams) { m_gameRenderParams = gameRenderParams; } void ChunkGridRenderStage::init(vui::GameWindow* window VORB_MAYBE_UNUSED, StaticLoadContext& context VORB_MAYBE_UNUSED) { m_vao = 0; m_vbo = 0; m_ibo = 0; } /// NOTE: There is a race condition with _chunkSlots here, but since _chunkSlots is a read only vector, /// it should not cause a crash. However data may be partially incorrect. void ChunkGridRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { if (!m_isActive) return; if (!m_state) return; const std::vector& chunkData = m_state->debugChunkData; // Element pattern const ui32 elementBuffer[24] = { 0, 1, 0, 2, 1, 3, 2, 3, 4, 5, 4, 6, 5, 7, 6, 7, 0, 4, 1, 5, 2, 6, 3, 7 }; // The mesh that is built from the chunks // Build the mesh ColorRGBA8 color; // Used to build each grid std::vector vertices(chunkData.size() * 8); std::vector indices(chunkData.size() * 24); int numVertices = 0; int numIndices = 0; f32v3 posOffset; for (auto& data : chunkData) { posOffset = f32v3(f64v3(data.voxelPosition) - m_gameRenderParams->chunkCamera->getPosition()); if (true /*((chunk->mesh && chunk->mesh->inFrustum) || m_gameRenderParams->chunkCamera->sphereInFrustum(posOffset + f32v3(CHUNK_WIDTH / 2), 28.0f))*/) { switch (data.genLevel) { case GEN_DONE: color = ColorRGBA8(0, 0, 255, 255); break; case GEN_FLORA: color = ColorRGBA8(0, 255, 0, 255); break; default: color = ColorRGBA8(255, 0, 0, 255); break; } for (int i = 0; i < 8; i++) { vertices[numVertices + i].color = color; vertices[numVertices + i].uv = f32v2(0.0f, 0.0f); } // Build the indices for (int i = 0; i < 24; i++) { indices.push_back(numVertices + elementBuffer[i]); } // Build the vertices const f32 gmin = 0.01f; const f32 gmax = 31.99f; vertices[numVertices + 0].position = f32v3(gmin, gmin, gmin) + posOffset; vertices[numVertices + 1].position = f32v3(gmax, gmin, gmin) + posOffset; vertices[numVertices + 2].position = f32v3(gmin, gmin, gmax) + posOffset; vertices[numVertices + 3].position = f32v3(gmax, gmin, gmax) + posOffset; vertices[numVertices + 4].position = f32v3(gmin, gmax, gmin) + posOffset; vertices[numVertices + 5].position = f32v3(gmax, gmax, gmin) + posOffset; vertices[numVertices + 6].position = f32v3(gmin, gmax, gmax) + posOffset; vertices[numVertices + 7].position = f32v3(gmax, gmax, gmax) + posOffset; numVertices += 8; numIndices += 24; } } // Check for non-empty mesh then draw if(numVertices != 0) drawGrid(vertices, indices); } void ChunkGridRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { if(m_vao != 0) glDeleteVertexArrays(1, &m_vao); if(m_vbo != 0) glDeleteBuffers(1, &m_vbo); if(m_ibo != 0) glDeleteBuffers(1, &m_ibo); } void ChunkGridRenderStage::drawGrid(std::vector vertices, std::vector indices) { if(m_vao == 0) glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); // Generate and bind VBO if(m_vbo == 0) glGenBuffers(1, &m_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_vbo); // Generate and bind element buffer if(m_ibo == 0) glGenBuffers(1, &m_ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo); // Set attribute arrays glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); // Set attribute pointers glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(ChunkGridVertex), offsetptr(ChunkGridVertex, position)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(ChunkGridVertex), offsetptr(ChunkGridVertex, color)); glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(ChunkGridVertex), offsetptr(ChunkGridVertex, uv)); // Unbind VAO glBindVertexArray(0); // Upload the data glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(ChunkGridVertex)* vertices.size(), nullptr, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ChunkGridVertex)* vertices.size(), vertices.data()); glBindBuffer(GL_ARRAY_BUFFER, 0); // GLuint numVertices = vertices.size(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ui32)* indices.size(), nullptr, GL_STATIC_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(ui32)* indices.size(), indices.data()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); GLsizei numIndices = (GLsizei)indices.size(); // Lazily initialize shader if(!m_program.isCreated()) m_program = ShaderLoader::createProgram("ChunkLine", VERT_SRC, FRAG_SRC); // Bind the program m_program.use(); // Set Matrix glUniformMatrix4fv(m_program.getUniform("MVP"), 1, GL_FALSE, &(m_gameRenderParams->chunkCamera->getViewProjectionMatrix()[0][0])); // Draw the grid // Bind the VAO glBindVertexArray(m_vao); // Perform draw call glDrawElements(GL_LINES, numIndices, GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); // Unuse the program m_program.unuse(); } ================================================ FILE: SoA/ChunkGridRenderStage.h ================================================ /// /// ChunkGridRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 14 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// This file provides the chunk grid render stage, /// which is a debug rendering stage for chunk state /// #pragma once #ifndef ChunkGridRenderStage_h__ #define ChunkGridRenderStage_h__ #include "IRenderStage.h" #include "MTRenderState.h" #include class ChunkGrid; class GameRenderParams; class ChunkMemoryManager; struct ChunkGridVertex { public: f32v3 position; f32v2 uv; color4 color; }; class ChunkGridRenderStage : public IRenderStage { public: void hook(const GameRenderParams* gameRenderParams); // Draws the render stage void setState(const MTRenderState* state) { m_state = state; } virtual void init(vui::GameWindow* window, StaticLoadContext& context) override; virtual void render(const Camera* camera) override; virtual void dispose(StaticLoadContext& context) override; private: void drawGrid(std::vector vertices, std::vector indices); vg::GLProgram m_program; const GameRenderParams* m_gameRenderParams = nullptr; ///< Handle to some shared parameters const MTRenderState* m_state = nullptr; GLuint m_vbo; GLuint m_ibo; GLuint m_vao; }; #endif // ChunkGridRenderStage_h__ ================================================ FILE: SoA/ChunkHandle.h ================================================ // // ChunkHandle.h // Seed of Andromeda // // Created by Benjamin Arnold on 30 Jul 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Does ref counting. // #pragma once #ifndef ChunkHandle_h__ #include "ChunkID.h" class Chunk; class ChunkAccessor; class ChunkHandle { friend class ChunkAccessor; public: ChunkHandle() : m_chunk(nullptr), m_id({}), m_acquired(false){ // Empty } ChunkHandle(const ChunkHandle& other); ChunkHandle& operator= (const ChunkHandle& other); ChunkHandle(ChunkHandle&& other) : m_chunk(other.m_chunk), m_id(other.m_id), m_acquired(other.m_acquired) { other.m_acquired = false; other.m_chunk = nullptr; other.m_id = 0; } ChunkHandle& operator= (ChunkHandle&& other) { m_acquired = other.m_acquired; m_chunk = other.m_chunk; m_id = other.m_id; other.m_acquired = false; other.m_chunk = nullptr; other.m_id.id = 0; return *this; } void acquireSelf(); ChunkHandle acquire(); void release(); bool isAquired() const { return m_acquired; } operator Chunk&() { return *m_chunk; } operator const Chunk&() const { return *m_chunk; } operator Chunk*() { return m_chunk; } operator const Chunk*() const { return m_chunk; } Chunk* operator->() { return m_chunk; } const Chunk* operator->() const { return m_chunk; } const ChunkID& getID() const { return m_id; } protected: union { Chunk* m_chunk; ChunkAccessor* m_accessor; }; ChunkID m_id; bool m_acquired; }; #endif // ChunkHandle_h__ ================================================ FILE: SoA/ChunkID.h ================================================ // // ChunkID.h // Seed of Andromeda // // Created by Benjamin Arnold on 3 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // 64 bit ID type for Chunks. // #pragma once #ifndef ChunkID_h__ #define ChunkID_h__ #include "Vorb/types.h" // Chunk ID, derived from position. struct ChunkID { ChunkID() : id(0) {}; ChunkID(i32 x, i32 y, i32 z) : x(x), y(y), z(z) {}; ChunkID(const i32v3& p) : x(p.x), y(p.y), z(p.z) {}; ChunkID(ui64 id) : id(id) {}; union { struct { i64 x : 24; i64 y : 16; i64 z : 24; }; ui64 id; }; operator ui64() const { return id; } }; // Hash for ID template <> struct std::hash { size_t operator()(const ChunkID& id) const { std::hash h; return h(id.id); } }; static_assert(sizeof(ChunkID) == 8, "ChunkID is not 64 bits"); #endif // ChunkID_h__ ================================================ FILE: SoA/ChunkIOManager.cpp ================================================ #include "stdafx.h" #include "ChunkIOManager.h" #ifdef VORB_OS_WINDOWS #include //for mkdir windows #include #endif #include #include #include #include #include //#include #include "BlockData.h" #include "Chunk.h" #include "Errors.h" #include "GameManager.h" #include "SoaOptions.h" ChunkIOManager::ChunkIOManager(const nString& saveDir) : _regionFileManager(saveDir) { _isThreadFinished = 0; readWriteThread = NULL; _shouldDisableLoading = 0; } ChunkIOManager::~ChunkIOManager() { onQuit(); } void ChunkIOManager::clear() { Chunk* tmp; _queueLock.lock(); //flush queues while (chunksToLoad.try_dequeue(tmp)); _queueLock.unlock(); while (chunksToSave.peek() != nullptr); std::this_thread::sleep_for(std::chrono::milliseconds(30)); while (finishedLoadChunks.try_dequeue(tmp)); } void ChunkIOManager::addToSaveList(Chunk* ch VORB_UNUSED) { //if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ // ch->dirty = 0; // ch->inSaveThread = 1; // chunksToSave.enqueue(ch); // _cond.notify_one(); //} } void ChunkIOManager::addToSaveList(std::vector &chunks VORB_UNUSED) { /* NChunk* ch; for (size_t i = 0; i < chunks.size(); i++){ ch = chunks[i]; if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->inSaveThread = 1; ch->dirty = 0; chunksToSave.enqueue(ch); } } _cond.notify_one();*/ } void ChunkIOManager::addToLoadList(Chunk* ch VORB_UNUSED) { /* if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->loadStatus = 0; ch->inLoadThread = 1; chunksToLoad.enqueue(ch); _cond.notify_one(); }*/ } void ChunkIOManager::addToLoadList(std::vector &chunks VORB_UNUSED) { /* NChunk* ch; if (_shouldDisableLoading) { for (size_t i = 0; i < chunks.size(); i++){ chunks[i]->loadStatus = 2; finishedLoadChunks.enqueue(chunks[i]); } return; } for (size_t i = 0; i < chunks.size(); i++){ ch = chunks[i]; if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->inLoadThread = true; chunksToLoad.enqueue(ch); } else{ std::cout << "ERROR: Tried to add chunk to load list and its in a thread! : " << (int)ch->inSaveThread << " " << (int)ch->inLoadThread << " " << ch->voxelPosition.z << std::endl; } } _cond.notify_one();*/ } void ChunkIOManager::readWriteChunks() { //std::unique_lock queueLock(_queueLock); //NChunk* ch; //nString reg; //while (!_isDone){ // if (_isDone){ // _regionFileManager.clear(); // queueLock.unlock(); // _isThreadFinished = 1; // return; // } // _regionFileManager.flush(); // _cond.wait(queueLock); //wait for a notification that queue is not empty // if (_isDone){ // _regionFileManager.clear(); // _isThreadFinished = 1; // queueLock.unlock(); // return; // } // queueLock.unlock(); // // All tasks // while (chunksToLoad.try_dequeue(ch) || chunksToSave.try_dequeue(ch)) { // if (ch->getState() == ChunkStates::LOAD) { // if (1 || _regionFileManager.tryLoadChunk(ch) == false) { // ch->loadStatus = 1; // } // finishedLoadChunks.enqueue(ch); // } else { //save // // _regionFileManager.saveChunk(ch); // ch->inSaveThread = 0; //race condition? // } // // } // queueLock.lock(); //} } void ChunkIOManager::beginThread() { _isDone = 0; _isThreadFinished = 0; readWriteThread = NULL; readWriteThread = new std::thread(&ChunkIOManager::readWriteChunks, this); } void ChunkIOManager::onQuit() { clear(); _queueLock.lock(); _isDone = 1; _queueLock.unlock(); _cond.notify_one(); if (readWriteThread != NULL && readWriteThread->joinable()) readWriteThread->join(); delete readWriteThread; readWriteThread = NULL; } bool ChunkIOManager::saveVersionFile() { return _regionFileManager.saveVersionFile(); } bool ChunkIOManager::checkVersion() { return _regionFileManager.checkVersion(); } ================================================ FILE: SoA/ChunkIOManager.h ================================================ #pragma once #include #include #include #include #include "RegionFileManager.h" #include "readerwriterqueue.h" class Chunk; class ChunkIOManager{ public: ChunkIOManager(const nString& saveDir); ~ChunkIOManager(); void clear(); void addToSaveList(Chunk* ch); void addToSaveList(std::vector& chunks); void addToLoadList(Chunk* ch); void addToLoadList(std::vector& chunks); void beginThread(); void onQuit(); void setDisableLoading(bool disableLoading) { _shouldDisableLoading = disableLoading; } bool saveVersionFile(); bool checkVersion(); moodycamel::ReaderWriterQueue chunksToLoad; moodycamel::ReaderWriterQueue chunksToSave; std::thread* readWriteThread; moodycamel::ReaderWriterQueue finishedLoadChunks; private: RegionFileManager _regionFileManager; void readWriteChunks(); //used by the thread std::mutex _queueLock; std::condition_variable _cond; bool _isDone; bool _isThreadFinished; bool _shouldDisableLoading; }; ================================================ FILE: SoA/ChunkMesh.cpp ================================================ #include "stdafx.h" #include "ChunkMesh.h" #include "BlockData.h" #include "Chunk.h" #include "ChunkMeshTask.h" KEG_ENUM_DEF(MeshType, MeshType, e) { e.addValue("none", MeshType::NONE); e.addValue("cube", MeshType::BLOCK); e.addValue("leaves", MeshType::LEAVES); e.addValue("triangle", MeshType::TRIANGLE); e.addValue("cross", MeshType::CROSSFLORA); e.addValue("liquid", MeshType::LIQUID); e.addValue("flat", MeshType::FLAT); } ChunkMeshData::ChunkMeshData() : type(MeshTaskType::DEFAULT) { // Empty } ChunkMeshData::ChunkMeshData(MeshTaskType type) : type(type) { // Empty } void ChunkMeshData::addTransQuad(const i8v3& pos) { transQuadPositions.push_back(pos); int size = transQuadIndices.size(); transQuadIndices.resize(size + 6); transQuadIndices[size++] = transVertIndex; transQuadIndices[size++] = transVertIndex + 1; transQuadIndices[size++] = transVertIndex + 2; transQuadIndices[size++] = transVertIndex + 2; transQuadIndices[size++] = transVertIndex + 3; transQuadIndices[size] = transVertIndex; transVertIndex += 4; } ================================================ FILE: SoA/ChunkMesh.h ================================================ #pragma once #include "Vertex.h" #include "BlockTextureMethods.h" #include "ChunkHandle.h" #include #include enum class MeshType { NONE, BLOCK, LEAVES, TRIANGLE, CROSSFLORA, LIQUID, FLAT }; KEG_ENUM_DECL(MeshType); enum class MeshTaskType; class Block; class Chunk; class ChunkGridData; class ChunkMesh; class ChunkMeshTask; class ChunkMeshRenderData { public: // TODO(Ben): These can be ui16 i32 pxVboOff = 0; i32 pxVboSize = 0; i32 nxVboOff = 0; i32 nxVboSize = 0; i32 pzVboOff = 0; i32 pzVboSize = 0; i32 nzVboOff = 0; i32 nzVboSize = 0; i32 pyVboOff = 0; i32 pyVboSize = 0; i32 nyVboOff = 0; i32 nyVboSize = 0; i32 transVboSize = 0; i32 cutoutVboSize = 0; i32 highestY = INT_MIN; i32 lowestY = INT_MAX; i32 highestX = INT_MIN; i32 lowestX = INT_MAX; i32 highestZ = INT_MIN; i32 lowestZ = INT_MAX; ui32 indexSize = 0; ui32 waterIndexSize = 0; }; struct VoxelQuad { VoxelQuad() {} VoxelQuad(VoxelQuad const &that) { for(size_t i=0; i<4; i++) verts[i]=that.verts[i]; } ~VoxelQuad() { v.v0.BlockVertex::~BlockVertex(); v.v1.BlockVertex::~BlockVertex(); v.v2.BlockVertex::~BlockVertex(); v.v3.BlockVertex::~BlockVertex(); } union { struct{ BlockVertex v0; BlockVertex v1; BlockVertex v2; union { BlockVertex v3; ui16 replaceQuad; // Quad that replaces this quad }; } v; BlockVertex verts[4]; }; }; class ChunkMeshData { public: ChunkMeshData(); ChunkMeshData(MeshTaskType type); void addTransQuad(const i8v3& pos); ChunkMeshRenderData chunkMeshRenderData; // TODO(Ben): Could use a contiguous buffer for this? std::vector opaqueQuads; std::vector transQuads; std::vector cutoutQuads; std::vector waterVertices; MeshTaskType type; //*** Transparency info for sorting *** ui32 transVertIndex = 0; std::vector transQuadPositions; std::vector transQuadIndices; }; #define ACTIVE_MESH_INDEX_NONE UINT_MAX class ChunkMesh { public: ChunkMesh() : vboID(0), waterVboID(0), cutoutVboID(0), transVboID(0), vaoID(0), transVaoID(0), cutoutVaoID(0), waterVaoID(0) {} ChunkMeshRenderData renderData; union { struct { VGVertexBuffer vboID; VGVertexBuffer waterVboID; VGVertexBuffer cutoutVboID; VGVertexBuffer transVboID; }; VGVertexBuffer vbos[4]; }; union { struct { VGVertexArray vaoID; VGVertexArray transVaoID; VGVertexArray cutoutVaoID; VGVertexArray waterVaoID; }; VGVertexArray vaos[4]; }; f64 distance2 = 32.0; f64v3 position; ui32 activeMeshesIndex = ACTIVE_MESH_INDEX_NONE; ///< Index into active meshes array ui32 updateVersion; bool inFrustum = false; bool needsSort = true; ChunkID id; //*** Transparency info for sorting *** VGIndexBuffer transIndexID = 0; std::vector transQuadPositions; std::vector transQuadIndices; }; ================================================ FILE: SoA/ChunkMeshManager.cpp ================================================ #include "stdafx.h" #include "ChunkMeshManager.h" #include "ChunkMesh.h" #include "ChunkMeshTask.h" #include "ChunkMesher.h" #include "ChunkRenderer.h" #include "SpaceSystemComponents.h" #include "soaUtils.h" #define MAX_UPDATES_PER_FRAME 300 ChunkMeshManager::ChunkMeshManager(vcore::ThreadPool* threadPool, BlockPack* blockPack) { m_threadPool = threadPool; m_blockPack = blockPack; SpaceSystemAssemblages::onAddSphericalVoxelComponent += makeDelegate(this, &ChunkMeshManager::onAddSphericalVoxelComponent); SpaceSystemAssemblages::onRemoveSphericalVoxelComponent += makeDelegate(this, &ChunkMeshManager::onRemoveSphericalVoxelComponent); } void ChunkMeshManager::update(const f64v3& cameraPosition, bool shouldSort) { ChunkMeshUpdateMessage updateBuffer[MAX_UPDATES_PER_FRAME]; size_t numUpdates; if ((numUpdates = m_messages.try_dequeue_bulk(updateBuffer, MAX_UPDATES_PER_FRAME))) { for (size_t i = 0; i < numUpdates; i++) { updateMesh(updateBuffer[i]); } } // Update pending meshes { std::lock_guard l(m_lckPendingMesh); for (auto it = m_pendingMesh.begin(); it != m_pendingMesh.end();) { ChunkMeshTask* task = createMeshTask(it->second); if (task) { { std::lock_guard l(m_lckActiveChunks); auto iter=m_activeChunks.find(it->first); assert(iter!=m_activeChunks.end()); iter->second->updateVersion = it->second->updateVersion; } m_threadPool->addTask(task); it->second.release(); m_pendingMesh.erase(it++); } else { ++it; } } } // TODO(Ben): This is redundant with the chunk manager! Find a way to share! (Pointer?) updateMeshDistances(cameraPosition); if (shouldSort) { } } void ChunkMeshManager::destroy() { std::vector ().swap(m_activeChunkMeshes); moodycamel::ConcurrentQueue().swap(m_messages); std::unordered_map().swap(m_activeChunks); } ChunkMesh* ChunkMeshManager::createMesh(ChunkHandle& h) { ChunkMesh* mesh; { // Get a free mesh std::lock_guard l(m_lckMeshRecycler); mesh = m_meshRecycler.create(); } mesh->id = h.getID(); // Set the position mesh->position = h->m_voxelPosition; // Zero buffers memset(mesh->vbos, 0, sizeof(mesh->vbos)); memset(mesh->vaos, 0, sizeof(mesh->vaos)); mesh->transIndexID = 0; mesh->activeMeshesIndex = ACTIVE_MESH_INDEX_NONE; { // Register chunk as active and give it a mesh std::lock_guard l(m_lckActiveChunks); m_activeChunks[h.getID()] = mesh; } return mesh; } ChunkMeshTask* ChunkMeshManager::createMeshTask(ChunkHandle& chunk) { ChunkHandle& left = chunk->neighbor.left; ChunkHandle& right = chunk->neighbor.right; ChunkHandle& bottom = chunk->neighbor.bottom; ChunkHandle& top = chunk->neighbor.top; ChunkHandle& back = chunk->neighbor.back; ChunkHandle& front = chunk->neighbor.front; if (!left.isAquired() || !right.isAquired() || !back.isAquired() || !front.isAquired() || !bottom.isAquired() || !top.isAquired()) { std::cout << "NOT ACQUIRED"; } if (left->genLevel != GEN_DONE || right->genLevel != GEN_DONE || back->genLevel != GEN_DONE || front->genLevel != GEN_DONE || bottom->genLevel != GEN_DONE || top->genLevel != GEN_DONE) return nullptr; // TODO(Ben): Recycler ChunkMeshTask* meshTask = new ChunkMeshTask; meshTask->init(chunk, MeshTaskType::DEFAULT, m_blockPack, this); // Set dependencies meshTask->neighborHandles[NEIGHBOR_HANDLE_LEFT] = left.acquire(); meshTask->neighborHandles[NEIGHBOR_HANDLE_RIGHT] = right.acquire(); meshTask->neighborHandles[NEIGHBOR_HANDLE_FRONT] = front.acquire(); meshTask->neighborHandles[NEIGHBOR_HANDLE_BACK] = back.acquire(); meshTask->neighborHandles[NEIGHBOR_HANDLE_TOP] = top.acquire(); meshTask->neighborHandles[NEIGHBOR_HANDLE_BOT] = bottom.acquire(); return meshTask; } void ChunkMeshManager::disposeMesh(ChunkMesh* mesh) { // De-allocate buffer objects glDeleteBuffers(4, mesh->vbos); glDeleteVertexArrays(4, mesh->vaos); if (mesh->transIndexID) glDeleteBuffers(1, &mesh->transIndexID); { // Remove from mesh list std::lock_guard l(lckActiveChunkMeshes); if (mesh->activeMeshesIndex != ACTIVE_MESH_INDEX_NONE) { m_activeChunkMeshes[mesh->activeMeshesIndex] = m_activeChunkMeshes.back(); m_activeChunkMeshes[mesh->activeMeshesIndex]->activeMeshesIndex = mesh->activeMeshesIndex; m_activeChunkMeshes.pop_back(); mesh->activeMeshesIndex = ACTIVE_MESH_INDEX_NONE; } } { // Release the mesh std::lock_guard l(m_lckMeshRecycler); m_meshRecycler.recycle(mesh); } } void ChunkMeshManager::updateMesh(ChunkMeshUpdateMessage& message) { ChunkMesh *mesh; { // Get the mesh object std::lock_guard l(m_lckActiveChunks); auto it = m_activeChunks.find(message.chunkID); if (it == m_activeChunks.end()) { delete message.meshData; return; /// The mesh was already released, so ignore! } mesh = it->second; } if (ChunkMesher::uploadMeshData(*mesh, message.meshData)) { // Add to active list if its not there std::lock_guard l(lckActiveChunkMeshes); if (mesh->activeMeshesIndex == ACTIVE_MESH_INDEX_NONE) { mesh->activeMeshesIndex = m_activeChunkMeshes.size(); mesh->updateVersion = 0; m_activeChunkMeshes.push_back(mesh); } } else { // Remove from active list std::lock_guard l(lckActiveChunkMeshes); if (mesh->activeMeshesIndex != ACTIVE_MESH_INDEX_NONE) { m_activeChunkMeshes[mesh->activeMeshesIndex] = m_activeChunkMeshes.back(); m_activeChunkMeshes[mesh->activeMeshesIndex]->activeMeshesIndex = mesh->activeMeshesIndex; m_activeChunkMeshes.pop_back(); mesh->activeMeshesIndex = ACTIVE_MESH_INDEX_NONE; } } // TODO(Ben): come on... delete message.meshData; } void ChunkMeshManager::updateMeshDistances(const f64v3& cameraPosition) { static const f64v3 CHUNK_DIMS(CHUNK_WIDTH); // TODO(Ben): Spherical instead? std::lock_guard l(lckActiveChunkMeshes); for (auto& mesh : m_activeChunkMeshes) { //update distances for all chunk meshes //calculate distance f64v3 closestPoint = getClosestPointOnAABB(cameraPosition, mesh->position, CHUNK_DIMS); // Omit sqrt for faster calculation mesh->distance2 = selfDot(closestPoint - cameraPosition); } } void ChunkMeshManager::onAddSphericalVoxelComponent(Sender s VORB_MAYBE_UNUSED, SphericalVoxelComponent& cmp, vecs::EntityID e VORB_MAYBE_UNUSED) { for (ui32 i = 0; i < 6; i++) { for (ui32 j = 0; j < cmp.chunkGrids[i].numGenerators; j++) { cmp.chunkGrids[i].generators[j].onGenFinish += makeDelegate(this, &ChunkMeshManager::onGenFinish); } cmp.chunkGrids[i].onNeighborsAcquire += makeDelegate(this, &ChunkMeshManager::onNeighborsAcquire); cmp.chunkGrids[i].onNeighborsRelease += makeDelegate(this, &ChunkMeshManager::onNeighborsRelease); Chunk::DataChange += makeDelegate(this, &ChunkMeshManager::onDataChange); } } void ChunkMeshManager::onRemoveSphericalVoxelComponent(Sender s VORB_MAYBE_UNUSED, SphericalVoxelComponent& cmp, vecs::EntityID e VORB_MAYBE_UNUSED) { for (ui32 i = 0; i < 6; i++) { for (ui32 j = 0; j < cmp.chunkGrids[i].numGenerators; j++) { cmp.chunkGrids[i].generators[j].onGenFinish -= makeDelegate(this, &ChunkMeshManager::onGenFinish); } cmp.chunkGrids[i].onNeighborsAcquire -= makeDelegate(this, &ChunkMeshManager::onNeighborsAcquire); cmp.chunkGrids[i].onNeighborsRelease -= makeDelegate(this, &ChunkMeshManager::onNeighborsRelease); Chunk::DataChange -= makeDelegate(this, &ChunkMeshManager::onDataChange); } } void ChunkMeshManager::onGenFinish(Sender s VORB_MAYBE_UNUSED, ChunkHandle& chunk, ChunkGenLevel gen VORB_MAYBE_UNUSED) { // Check if can be meshed. if (chunk->genLevel == GEN_DONE && chunk->neighbor.left.isAquired() && chunk->numBlocks) { std::lock_guard l(m_lckPendingMesh); m_pendingMesh.emplace(chunk.getID(), chunk.acquire()); } } void ChunkMeshManager::onNeighborsAcquire(Sender s VORB_UNUSED, ChunkHandle& chunk) { createMesh(chunk); // Check if can be meshed. if (chunk->genLevel == GEN_DONE && chunk->numBlocks) { std::lock_guard l(m_lckPendingMesh); m_pendingMesh.emplace(chunk.getID(), chunk.acquire()); } } void ChunkMeshManager::onNeighborsRelease(Sender s VORB_MAYBE_UNUSED, ChunkHandle& chunk) { // Destroy message ChunkMesh* mesh; { std::lock_guard l(m_lckActiveChunks); auto it = m_activeChunks.find(chunk.getID()); if (it == m_activeChunks.end()) { return; } else { mesh = it->second; m_activeChunks.erase(it); } } { std::lock_guard l(m_lckPendingMesh); auto it = m_pendingMesh.find(chunk.getID()); if (it != m_pendingMesh.end()) { it->second.release(); m_pendingMesh.erase(it); } } disposeMesh(mesh); } void ChunkMeshManager::onDataChange(Sender s VORB_MAYBE_UNUSED, ChunkHandle& chunk) { // Have to have neighbors // TODO(Ben): Race condition with neighbor removal here. if (chunk->neighbor.left.isAquired()) { std::lock_guard l(m_lckPendingMesh); m_pendingMesh.emplace(chunk.getID(), chunk.acquire()); } } ================================================ FILE: SoA/ChunkMeshManager.h ================================================ /// /// ChunkMeshManager.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 26 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles the updating and deallocation of /// chunk meshes. /// #pragma once #ifndef ChunkMeshManager_h__ #define ChunkMeshManager_h__ #include "Vorb/concurrentqueue.h" #include "Chunk.h" #include "ChunkMesh.h" #include "SpaceSystemAssemblages.h" #include struct ChunkMeshUpdateMessage { ChunkID chunkID; ChunkMeshData* meshData = nullptr; }; class ChunkMeshManager { public: ChunkMeshManager(vcore::ThreadPool* threadPool, BlockPack* blockPack); /// Updates the meshManager, uploading any needed meshes void update(const f64v3& cameraPosition, bool shouldSort); /// Adds a mesh for updating void sendMessage(const ChunkMeshUpdateMessage& message) { m_messages.enqueue(message); } /// Destroys all meshes void destroy(); // Be sure to lock lckActiveChunkMeshes const std::vector & getChunkMeshes() { return m_activeChunkMeshes; } std::mutex lckActiveChunkMeshes; private: VORB_NON_COPYABLE(ChunkMeshManager); ChunkMesh* createMesh(ChunkHandle& h); ChunkMeshTask* createMeshTask(ChunkHandle& chunk); void disposeMesh(ChunkMesh* mesh); /// Uploads a mesh and adds to list if needed void updateMesh(ChunkMeshUpdateMessage& message); void updateMeshDistances(const f64v3& cameraPosition); /************************************************************************/ /* Event Handlers */ /************************************************************************/ void onAddSphericalVoxelComponent(Sender s, SphericalVoxelComponent& cmp, vecs::EntityID e); void onRemoveSphericalVoxelComponent(Sender s, SphericalVoxelComponent& cmp, vecs::EntityID e); void onGenFinish(Sender s, ChunkHandle& chunk, ChunkGenLevel gen); void onNeighborsAcquire(Sender s, ChunkHandle& chunk); void onNeighborsRelease(Sender s, ChunkHandle& chunk); void onDataChange(Sender s, ChunkHandle& chunk); /************************************************************************/ /* Members */ /************************************************************************/ std::vector m_activeChunkMeshes; ///< Meshes that should be drawn moodycamel::ConcurrentQueue m_messages; ///< Lock-free queue of messages BlockPack* m_blockPack = nullptr; vcore::ThreadPool* m_threadPool = nullptr; std::mutex m_lckPendingMesh; std::map m_pendingMesh; std::mutex m_lckMeshRecycler; PtrRecycler m_meshRecycler; std::mutex m_lckActiveChunks; std::unordered_map m_activeChunks; ///< Stores chunk IDs that have meshes }; #endif // ChunkMeshManager_h__ ================================================ FILE: SoA/ChunkMeshTask.cpp ================================================ #include "stdafx.h" #include "ChunkMeshTask.h" #include #include "BlockData.h" #include "BlockPack.h" #include "ChunkMeshManager.h" #include "ChunkMesher.h" #include "GameManager.h" #include "Chunk.h" #include "VoxelLightEngine.h" #include "VoxelUtils.h" void ChunkMeshTask::execute(WorkerData* workerData) { // Mesh updates are accompanied by light updates // TODO(Ben): Seems wasteful. if (workerData->voxelLightEngine == nullptr) { workerData->voxelLightEngine = new VoxelLightEngine(); } // TODO(Ben): Lighting // updateLight(workerData->voxelLightEngine); // Lazily allocate chunkMesher // TODO(Ben): Seems wasteful. if (workerData->chunkMesher == nullptr) { workerData->chunkMesher = new ChunkMesher; workerData->chunkMesher->init(blockPack); } // Prepare message ChunkMeshUpdateMessage msg; msg.chunkID = chunk.getID(); // Pre-processing workerData->chunkMesher->prepareDataAsync(chunk, neighborHandles); // Create the actual mesh msg.meshData = workerData->chunkMesher->createChunkMeshData(type); // Send it for update meshManager->sendMessage(msg); } void ChunkMeshTask::init(ChunkHandle& ch, MeshTaskType cType, const BlockPack* blockPack, ChunkMeshManager* meshManager) { type = cType; chunk = ch.acquire(); this->blockPack = blockPack; this->meshManager = meshManager; } // TODO(Ben): uhh void ChunkMeshTask::updateLight(VoxelLightEngine* voxelLightEngine VORB_UNUSED) { /* if (chunk->sunRemovalList.size()) { voxelLightEngine->calculateSunlightRemoval(chunk); } if (chunk->sunExtendList.size()) { voxelLightEngine->calculateSunlightExtend(chunk); } voxelLightEngine->calculateLight(chunk);*/ } ================================================ FILE: SoA/ChunkMeshTask.h ================================================ /// /// RenderTask.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// This file has the implementation of a render task for SoA /// #pragma once #ifndef RenderTask_h__ #define RenderTask_h__ #include #include "ChunkHandle.h" #include "Constants.h" #include "VoxPool.h" class Chunk; class ChunkGridData; class ChunkMesh; class ChunkMeshData; class ChunkMeshManager; class VoxelLightEngine; class BlockPack; enum class MeshTaskType { DEFAULT, LIQUID }; enum MeshNeighborHandles { NEIGHBOR_HANDLE_LEFT = 0, NEIGHBOR_HANDLE_RIGHT, NEIGHBOR_HANDLE_FRONT, NEIGHBOR_HANDLE_BACK, NEIGHBOR_HANDLE_TOP, NEIGHBOR_HANDLE_BOT, NUM_NEIGHBOR_HANDLES ///< Has to be last }; #define CHUNK_MESH_TASK_ID 0 // Represents A Mesh Creation Task class ChunkMeshTask : public vcore::IThreadPoolTask { public: ChunkMeshTask() : vcore::IThreadPoolTask(CHUNK_MESH_TASK_ID) {} // Executes the task void execute(WorkerData* workerData) override; // Initializes the task void init(ChunkHandle& ch, MeshTaskType cType, const BlockPack* blockPack, ChunkMeshManager* meshManager); MeshTaskType type; ChunkHandle chunk; ChunkMeshManager* meshManager = nullptr; const BlockPack* blockPack = nullptr; ChunkHandle neighborHandles[NUM_NEIGHBOR_HANDLES]; private: void updateLight(VoxelLightEngine* voxelLightEngine); }; #endif // RenderTask_h__ ================================================ FILE: SoA/ChunkMesher.cpp ================================================ #include "stdafx.h" #include "ChunkMesher.h" #include #include "Biome.h" #include "BlockData.h" #include #include #include "BlockPack.h" #include "Chunk.h" #include "ChunkMeshTask.h" #include "ChunkRenderer.h" #include "Errors.h" #include "GameManager.h" #include "SoaOptions.h" #include "VoxelBits.h" #include "VoxelMesher.h" #include "VoxelUtils.h" #define GETBLOCK(a) blocks->operator[](a) // const float LIGHT_MULT = 0.95f, LIGHT_OFFSET = -0.2f; // const int MAXLIGHT = 31; // Shorter aliases #define PADDED_WIDTH PADDED_CHUNK_WIDTH #define PADDED_LAYER PADDED_CHUNK_LAYER #define PADDED_SIZE PADDED_CHUNK_SIZE const int PADDED_WIDTH_M1 = PADDED_WIDTH - 1; #define NO_QUAD_INDEX 0xFFFF #define QUAD_SIZE 7 //#define USE_AO // Base texture index #define B_INDEX 0 // Overlay texture index #define O_INDEX 1 const int X_NEG = (int)vvox::Cardinal::X_NEG; const int X_POS = (int)vvox::Cardinal::X_POS; const int Y_NEG = (int)vvox::Cardinal::Y_NEG; const int Y_POS = (int)vvox::Cardinal::Y_POS; const int Z_NEG = (int)vvox::Cardinal::Z_NEG; const int Z_POS = (int)vvox::Cardinal::Z_POS; #define UV_0 128 #define UV_1 129 // Meshing constants //0 = x, 1 = y, 2 = z const int FACE_AXIS[6][2] = { { 2, 1 }, { 2, 1 }, { 0, 2 }, { 0, 2 }, { 0, 1 }, { 0, 1 } }; const int FACE_AXIS_SIGN[6][2] = { { 1, 1 }, { -1, 1 }, { 1, 1 }, { -1, 1 }, { -1, 1 }, { 1, 1 } }; PlanetHeightData ChunkMesher::defaultChunkHeightData[CHUNK_LAYER] = {}; void ChunkMesher::init(const BlockPack* blocks) { this->blocks = blocks; // Set up the texture params m_textureMethodParams[X_NEG][B_INDEX].init(this, PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, -1, X_NEG, B_INDEX); m_textureMethodParams[X_NEG][O_INDEX].init(this, PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, -1, X_NEG, O_INDEX); m_textureMethodParams[X_POS][B_INDEX].init(this, -PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, 1, X_POS, B_INDEX); m_textureMethodParams[X_POS][O_INDEX].init(this, -PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, 1, X_POS, O_INDEX); m_textureMethodParams[Y_NEG][B_INDEX].init(this, -1, -PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, Y_NEG, B_INDEX); m_textureMethodParams[Y_NEG][O_INDEX].init(this, -1, -PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, Y_NEG, O_INDEX); m_textureMethodParams[Y_POS][B_INDEX].init(this, 1, -PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, Y_POS, B_INDEX); m_textureMethodParams[Y_POS][O_INDEX].init(this, 1, -PADDED_CHUNK_WIDTH, PADDED_CHUNK_LAYER, Y_POS, O_INDEX); m_textureMethodParams[Z_NEG][B_INDEX].init(this, -1, PADDED_CHUNK_LAYER, -PADDED_CHUNK_WIDTH, Z_NEG, B_INDEX); m_textureMethodParams[Z_NEG][O_INDEX].init(this, -1, PADDED_CHUNK_LAYER, -PADDED_CHUNK_WIDTH, Z_NEG, O_INDEX); m_textureMethodParams[Z_POS][B_INDEX].init(this, 1, PADDED_CHUNK_LAYER, PADDED_CHUNK_WIDTH, Z_POS, B_INDEX); m_textureMethodParams[Z_POS][O_INDEX].init(this, 1, PADDED_CHUNK_LAYER, PADDED_CHUNK_WIDTH, Z_POS, O_INDEX); } void ChunkMesher::prepareData(const Chunk* chunk) { int x, y, z, off1, off2; const Chunk* left = chunk->neighbor.left; const Chunk* right = chunk->neighbor.right; const Chunk* bottom = chunk->neighbor.bottom; const Chunk* top = chunk->neighbor.top; const Chunk* back = chunk->neighbor.back; const Chunk* front = chunk->neighbor.front; int wc; int c = 0; i32v3 pos; wSize = 0; chunkVoxelPos = chunk->getVoxelPosition(); if (chunk->gridData) { m_chunkHeightData = chunk->gridData->heightData; } else { m_chunkHeightData = defaultChunkHeightData; } // TODO(Ben): Do this last so we can be queued for mesh longer? // TODO(Ben): Dude macro this or something. memset(blockData, 0, sizeof(blockData)); memset(tertiaryData, 0, sizeof(tertiaryData)); if (chunk->blocks.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { int s = 0; //block data auto& dataTree = chunk->blocks.getTree(); for (size_t i = 0; i < dataTree.size(); i++) { for (size_t j = 0; j < dataTree[i].length; j++) { c = dataTree[i].getStart() + j; getPosFromBlockIndex(c, pos); wc = (pos.y + 1)*PADDED_LAYER + (pos.z + 1)*PADDED_WIDTH + (pos.x + 1); blockData[wc] = dataTree[i].data; if (GETBLOCK(blockData[wc]).meshType == MeshType::LIQUID) { m_wvec[s++] = wc; } } } wSize = s; } else { int s = 0; for (y = 0; y < CHUNK_WIDTH; y++) { for (z = 0; z < CHUNK_WIDTH; z++) { for (x = 0; x < CHUNK_WIDTH; x++, c++) { wc = (y + 1)*PADDED_LAYER + (z + 1)*PADDED_WIDTH + (x + 1); blockData[wc] = chunk->blocks[c]; if (GETBLOCK(blockData[wc]).meshType == MeshType::LIQUID) { m_wvec[s++] = wc; } } } } wSize = s; } if (chunk->tertiary.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { //tertiary data c = 0; auto& dataTree = chunk->tertiary.getTree(); for (size_t i = 0; i < dataTree.size(); i++) { for (size_t j = 0; j < dataTree[i].length; j++) { c = dataTree[i].getStart() + j; getPosFromBlockIndex(c, pos); wc = (pos.y + 1)*PADDED_LAYER + (pos.z + 1)*PADDED_WIDTH + (pos.x + 1); tertiaryData[wc] = dataTree[i].data; } } } else { c = 0; for (y = 0; y < CHUNK_WIDTH; y++) { for (z = 0; z < CHUNK_WIDTH; z++) { for (x = 0; x < CHUNK_WIDTH; x++, c++) { wc = (y + 1)*PADDED_LAYER + (z + 1)*PADDED_WIDTH + (x + 1); tertiaryData[wc] = chunk->tertiary.get(c); } } } } if (left) { for (y = 1; y < PADDED_WIDTH - 1; y++) { for (z = 1; z < PADDED_WIDTH - 1; z++) { off1 = (z - 1)*CHUNK_WIDTH + (y - 1)*CHUNK_LAYER; off2 = z*PADDED_WIDTH + y*PADDED_LAYER; blockData[off2] = left->getBlockData(off1 + CHUNK_WIDTH - 1); tertiaryData[off2] = left->getTertiaryData(off1 + CHUNK_WIDTH - 1); } } } if (right) { for (y = 1; y < PADDED_WIDTH - 1; y++) { for (z = 1; z < PADDED_WIDTH - 1; z++) { off1 = (z - 1)*CHUNK_WIDTH + (y - 1)*CHUNK_LAYER; off2 = z*PADDED_WIDTH + y*PADDED_LAYER; blockData[off2 + PADDED_WIDTH - 1] = (right->getBlockData(off1)); tertiaryData[off2 + PADDED_WIDTH - 1] = right->getTertiaryData(off1); } } } if (bottom) { for (z = 1; z < PADDED_WIDTH - 1; z++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { off1 = (z - 1)*CHUNK_WIDTH + x - 1; off2 = z*PADDED_WIDTH + x; //data blockData[off2] = (bottom->getBlockData(CHUNK_SIZE - CHUNK_LAYER + off1)); //bottom tertiaryData[off2] = bottom->getTertiaryData(CHUNK_SIZE - CHUNK_LAYER + off1); } } } if (top) { for (z = 1; z < PADDED_WIDTH - 1; z++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { off1 = (z - 1)*CHUNK_WIDTH + x - 1; off2 = z*PADDED_WIDTH + x; blockData[off2 + PADDED_SIZE - PADDED_LAYER] = (top->getBlockData(off1)); //top tertiaryData[off2 + PADDED_SIZE - PADDED_LAYER] = top->getTertiaryData(off1); } } } if (back) { for (y = 1; y < PADDED_WIDTH - 1; y++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { off1 = (x - 1) + (y - 1)*CHUNK_LAYER; off2 = x + y*PADDED_LAYER; blockData[off2] = back->getBlockData(off1 + CHUNK_LAYER - CHUNK_WIDTH); tertiaryData[off2] = back->getTertiaryData(off1 + CHUNK_LAYER - CHUNK_WIDTH); } } } if (front) { for (y = 1; y < PADDED_WIDTH - 1; y++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { off1 = (x - 1) + (y - 1)*CHUNK_LAYER; off2 = x + y*PADDED_LAYER; blockData[off2 + PADDED_LAYER - PADDED_WIDTH] = (front->getBlockData(off1)); tertiaryData[off2 + PADDED_LAYER - PADDED_WIDTH] = front->getTertiaryData(off1); } } } } #define GET_EDGE_X(ch, sy, sz, dy, dz) \ { \ std::lock_guard l(ch->dataMutex); \ for (int x = 0; x < CHUNK_WIDTH; x++) { \ srcIndex = (sy) * CHUNK_LAYER + (sz) * CHUNK_WIDTH + x; \ destIndex = (dy) * PADDED_LAYER + (dz) * PADDED_WIDTH + (x + 1); \ blockData[destIndex] = ch->getBlockData(srcIndex); \ tertiaryData[destIndex] = ch->getTertiaryData(srcIndex); \ } \ } \ ch.release(); #define GET_EDGE_Y(ch, sx, sz, dx, dz) \ { \ std::lock_guard l(ch->dataMutex); \ for (int y = 0; y < CHUNK_WIDTH; y++) { \ srcIndex = y * CHUNK_LAYER + (sz) * CHUNK_WIDTH + (sx); \ destIndex = (y + 1) * PADDED_LAYER + (dz) * PADDED_WIDTH + (dx); \ blockData[destIndex] = ch->getBlockData(srcIndex); \ tertiaryData[destIndex] = ch->getTertiaryData(srcIndex); \ } \ } \ ch.release(); #define GET_EDGE_Z(ch, sx, sy, dx, dy) \ { \ std::lock_guard l(ch->dataMutex); \ for (int z = 0; z < CHUNK_WIDTH; z++) { \ srcIndex = z * CHUNK_WIDTH + (sy) * CHUNK_LAYER + (sx); \ destIndex = (z + 1) * PADDED_WIDTH + (dy) * PADDED_LAYER + (dx); \ blockData[destIndex] = ch->getBlockData(srcIndex); \ tertiaryData[destIndex] = ch->getTertiaryData(srcIndex); \ } \ } \ ch.release(); #define GET_CORNER(ch, sx, sy, sz, dx, dy, dz) \ srcIndex = (sy) * CHUNK_LAYER + (sz) * CHUNK_WIDTH + (sx); \ destIndex = (dy) * PADDED_LAYER + (dz) * PADDED_WIDTH + (dx); \ { \ std::lock_guard l(ch->dataMutex); \ blockData[destIndex] = ch->getBlockData(srcIndex); \ tertiaryData[destIndex] = ch->getTertiaryData(srcIndex); \ } \ ch.release(); void ChunkMesher::prepareDataAsync(ChunkHandle& chunk, ChunkHandle neighbors[NUM_NEIGHBOR_HANDLES]) { int x, y, z, srcIndex, destIndex; int wc; int c = 0; i32v3 pos; wSize = 0; chunkVoxelPos = chunk->getVoxelPosition(); if (chunk->gridData) { // If its async we copy to avoid storing a shared_ptr memcpy(heightDataBuffer, chunk->gridData->heightData, sizeof(heightDataBuffer)); m_chunkHeightData = heightDataBuffer; } else { m_chunkHeightData = defaultChunkHeightData; } // TODO(Ben): Do this last so we can be queued for mesh longer? // TODO(Ben): Dude macro this or something. { // Main chunk std::lock_guard l(chunk->dataMutex); if (chunk->blocks.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { int s = 0; //block data auto& dataTree = chunk->blocks.getTree(); for (size_t i = 0; i < dataTree.size(); i++) { for (size_t j = 0; j < dataTree[i].length; j++) { c = dataTree[i].getStart() + j; getPosFromBlockIndex(c, pos); wc = (pos.y + 1)*PADDED_LAYER + (pos.z + 1)*PADDED_WIDTH + (pos.x + 1); blockData[wc] = dataTree[i].data; if (GETBLOCK(blockData[wc]).meshType == MeshType::LIQUID) { m_wvec[s++] = wc; } } } wSize = s; } else { int s = 0; for (y = 0; y < CHUNK_WIDTH; y++) { for (z = 0; z < CHUNK_WIDTH; z++) { for (x = 0; x < CHUNK_WIDTH; x++, c++) { wc = (y + 1)*PADDED_LAYER + (z + 1)*PADDED_WIDTH + (x + 1); blockData[wc] = chunk->blocks[c]; if (GETBLOCK(blockData[wc]).meshType == MeshType::LIQUID) { m_wvec[s++] = wc; } } } } wSize = s; } if (chunk->tertiary.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { //tertiary data c = 0; auto& dataTree = chunk->tertiary.getTree(); for (size_t i = 0; i < dataTree.size(); i++) { for (size_t j = 0; j < dataTree[i].length; j++) { c = dataTree[i].getStart() + j; getPosFromBlockIndex(c, pos); wc = (pos.y + 1)*PADDED_LAYER + (pos.z + 1)*PADDED_WIDTH + (pos.x + 1); tertiaryData[wc] = dataTree[i].data; } } } else { c = 0; for (y = 0; y < CHUNK_WIDTH; y++) { for (z = 0; z < CHUNK_WIDTH; z++) { for (x = 0; x < CHUNK_WIDTH; x++, c++) { wc = (y + 1)*PADDED_LAYER + (z + 1)*PADDED_WIDTH + (x + 1); tertiaryData[wc] = chunk->tertiary.get(c); } } } } } chunk.release(); ChunkHandle& left = neighbors[NEIGHBOR_HANDLE_LEFT]; { // Left std::lock_guard l(left->dataMutex); for (y = 1; y < PADDED_WIDTH - 1; y++) { for (z = 1; z < PADDED_WIDTH - 1; z++) { srcIndex = (z - 1)*CHUNK_WIDTH + (y - 1)*CHUNK_LAYER; destIndex = z*PADDED_WIDTH + y*PADDED_LAYER; blockData[destIndex] = left->getBlockData(srcIndex + CHUNK_WIDTH - 1); tertiaryData[destIndex] = left->getTertiaryData(srcIndex + CHUNK_WIDTH - 1); } } } left.release(); ChunkHandle& right = neighbors[NEIGHBOR_HANDLE_RIGHT]; { // Right std::lock_guard l(right->dataMutex); for (y = 1; y < PADDED_WIDTH - 1; y++) { for (z = 1; z < PADDED_WIDTH - 1; z++) { srcIndex = (z - 1)*CHUNK_WIDTH + (y - 1)*CHUNK_LAYER; destIndex = z*PADDED_WIDTH + y*PADDED_LAYER + PADDED_WIDTH - 1; blockData[destIndex] = (right->getBlockData(srcIndex)); tertiaryData[destIndex] = right->getTertiaryData(srcIndex); } } } right.release(); ChunkHandle& bottom = neighbors[NEIGHBOR_HANDLE_BOT]; { // Bottom std::lock_guard l(bottom->dataMutex); for (z = 1; z < PADDED_WIDTH - 1; z++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { srcIndex = (z - 1)*CHUNK_WIDTH + x - 1 + CHUNK_SIZE - CHUNK_LAYER; destIndex = z*PADDED_WIDTH + x; //data blockData[destIndex] = (bottom->getBlockData(srcIndex)); //bottom tertiaryData[destIndex] = bottom->getTertiaryData(srcIndex); } } } bottom.release(); ChunkHandle& top = neighbors[NEIGHBOR_HANDLE_TOP]; { // Top std::lock_guard l(top->dataMutex); for (z = 1; z < PADDED_WIDTH - 1; z++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { srcIndex = (z - 1)*CHUNK_WIDTH + x - 1; destIndex = z*PADDED_WIDTH + x + PADDED_SIZE - PADDED_LAYER; blockData[destIndex] = (top->getBlockData(srcIndex)); //top tertiaryData[destIndex] = top->getTertiaryData(srcIndex); } } } top.release(); ChunkHandle& back = neighbors[NEIGHBOR_HANDLE_BACK]; { // Back std::lock_guard l(back->dataMutex); for (y = 1; y < PADDED_WIDTH - 1; y++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { srcIndex = (x - 1) + (y - 1)*CHUNK_LAYER + CHUNK_LAYER - CHUNK_WIDTH; destIndex = x + y*PADDED_LAYER; blockData[destIndex] = back->getBlockData(srcIndex); tertiaryData[destIndex] = back->getTertiaryData(srcIndex); } } } back.release(); ChunkHandle& front = neighbors[NEIGHBOR_HANDLE_FRONT]; { // Front std::lock_guard l(front->dataMutex); for (y = 1; y < PADDED_WIDTH - 1; y++) { for (x = 1; x < PADDED_WIDTH - 1; x++) { srcIndex = (x - 1) + (y - 1)*CHUNK_LAYER; destIndex = x + y*PADDED_LAYER + PADDED_LAYER - PADDED_WIDTH; blockData[destIndex] = front->getBlockData(srcIndex); tertiaryData[destIndex] = front->getTertiaryData(srcIndex); } } } front.release(); // Clone edge data // TODO(Ben): Light gradient calc // X horizontal rows for (x = 1; x < PADDED_WIDTH_M1; x++) { // Bottom Back srcIndex = x + PADDED_WIDTH; destIndex = x; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Bottom Front srcIndex = x + PADDED_LAYER - PADDED_WIDTH * 2; destIndex = srcIndex + PADDED_WIDTH; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Top Back srcIndex = x + PADDED_WIDTH + PADDED_SIZE - PADDED_LAYER; destIndex = srcIndex - PADDED_WIDTH; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Top Front srcIndex = x + PADDED_SIZE - PADDED_WIDTH * 2; destIndex = srcIndex + PADDED_WIDTH; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; } // Z horizontal rows for (z = 1; z < PADDED_WIDTH_M1; z++) { int zi = z * PADDED_WIDTH; // Bottom Left srcIndex = zi + 1; destIndex = zi; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Bottom Right srcIndex = zi + PADDED_WIDTH - 2; destIndex = srcIndex + 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Top Left srcIndex = zi + PADDED_SIZE - PADDED_LAYER + 1; destIndex = srcIndex - 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Top Right srcIndex = zi + PADDED_SIZE - PADDED_LAYER + PADDED_WIDTH - 2; destIndex = srcIndex + 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; } // Vertical columns for (y = 0; y < PADDED_WIDTH; y++) { int yi = y * PADDED_LAYER; // Left back srcIndex = yi + 1; destIndex = yi; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Left front srcIndex = yi + PADDED_LAYER - PADDED_WIDTH + 1; destIndex = srcIndex - 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Right back srcIndex = yi + PADDED_WIDTH - 2; destIndex = srcIndex + 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; // Right front srcIndex = yi + PADDED_LAYER - 2; destIndex = srcIndex + 1; blockData[destIndex] = blockData[srcIndex]; tertiaryData[destIndex] = tertiaryData[srcIndex]; } } CALLER_DELETE ChunkMeshData* ChunkMesher::createChunkMeshData(MeshTaskType type VORB_UNUSED) { m_numQuads = 0; m_highestY = 0; m_lowestY = 256; m_highestX = 0; m_lowestX = 256; m_highestZ = 0; m_lowestZ = 256; // Clear quad indices memset(m_quadIndices, 0xFF, sizeof(m_quadIndices)); for (int i = 0; i < 6; i++) { m_quads[i].clear(); } // TODO(Ben): Here? _waterVboVerts.clear(); // Stores the data for a chunk mesh // TODO(Ben): new is bad mkay m_chunkMeshData = new ChunkMeshData(MeshTaskType::DEFAULT); // Loop through blocks for (by = 0; by < CHUNK_WIDTH; by++) { for (bz = 0; bz < CHUNK_WIDTH; bz++) { for (bx = 0; bx < CHUNK_WIDTH; bx++) { // Get data for this voxel // TODO(Ben): Could optimize out -1 blockIndex = (by + 1) * PADDED_CHUNK_LAYER + (bz + 1) * PADDED_CHUNK_WIDTH + (bx + 1); blockID = blockData[blockIndex]; if (blockID == 0) continue; // Skip air blocks heightData = &m_chunkHeightData[bz * CHUNK_WIDTH + bx]; block = &blocks->operator[](blockID); // TODO(Ben) Don't think bx needs to be member voxelPosOffset = ui8v3(bx * QUAD_SIZE, by * QUAD_SIZE, bz * QUAD_SIZE); switch (block->meshType) { case MeshType::BLOCK: addBlock(); break; case MeshType::LEAVES: case MeshType::CROSSFLORA: case MeshType::TRIANGLE: addFlora(); break; default: //No mesh, do nothing break; } } } } ChunkMeshRenderData& renderData = m_chunkMeshData->chunkMeshRenderData; // Get quad buffer to fill std::vector& finalQuads = m_chunkMeshData->opaqueQuads; finalQuads.resize(m_numQuads); // Copy the data // TODO(Ben): Could construct in place and not need ANY copying with 6 iterations? i32 index = 0; i32 sizes[6]; for (int i = 0; i < 6; i++) { std::vector& quads = m_quads[i]; int tmp = index; for (size_t j = 0; j < quads.size(); j++) { VoxelQuad& q = quads[j]; if (q.v.v0.mesherFlags & MESH_FLAG_ACTIVE) { finalQuads[index++] = q; } } sizes[i] = index - tmp; } // Swap flora quads renderData.cutoutVboSize = m_floraQuads.size() * INDICES_PER_QUAD; m_chunkMeshData->cutoutQuads.swap(m_floraQuads); m_highestY /= QUAD_SIZE; m_lowestY /= QUAD_SIZE; m_highestX /= QUAD_SIZE; m_lowestX /= QUAD_SIZE; m_highestZ /= QUAD_SIZE; m_lowestZ /= QUAD_SIZE; #define INDICES_PER_QUAD 6 if (finalQuads.size()) { renderData.nxVboOff = 0; renderData.nxVboSize = sizes[0] * INDICES_PER_QUAD; renderData.pxVboOff = renderData.nxVboSize; renderData.pxVboSize = sizes[1] * INDICES_PER_QUAD; renderData.nyVboOff = renderData.pxVboOff + renderData.pxVboSize; renderData.nyVboSize = sizes[2] * INDICES_PER_QUAD; renderData.pyVboOff = renderData.nyVboOff + renderData.nyVboSize; renderData.pyVboSize = sizes[3] * INDICES_PER_QUAD; renderData.nzVboOff = renderData.pyVboOff + renderData.pyVboSize; renderData.nzVboSize = sizes[4] * INDICES_PER_QUAD; renderData.pzVboOff = renderData.nzVboOff + renderData.nzVboSize; renderData.pzVboSize = sizes[5] * INDICES_PER_QUAD; renderData.indexSize = finalQuads.size() * INDICES_PER_QUAD; // Redundant renderData.highestX = m_highestX; renderData.lowestX = m_lowestX; renderData.highestY = m_highestY; renderData.lowestY = m_lowestY; renderData.highestZ = m_highestZ; renderData.lowestZ = m_lowestZ; } return m_chunkMeshData; } inline bool mapBufferData(GLuint& vboID, GLsizeiptr size, void* src, GLenum usage) { // Block Vertices if (vboID == 0) { glGenBuffers(1, &(vboID)); // Create the buffer ID } glBindBuffer(GL_ARRAY_BUFFER, vboID); glBufferData(GL_ARRAY_BUFFER, size, NULL, usage); void *v = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); if (v == NULL) return false; memcpy(v, src, size); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); return true; } bool ChunkMesher::uploadMeshData(ChunkMesh& mesh, ChunkMeshData* meshData) { bool canRender = false; //store the index data for sorting in the chunk mesh mesh.transQuadIndices.swap(meshData->transQuadIndices); mesh.transQuadPositions.swap(meshData->transQuadPositions); switch (meshData->type) { case MeshTaskType::DEFAULT: if (meshData->opaqueQuads.size()) { mapBufferData(mesh.vboID, meshData->opaqueQuads.size() * sizeof(VoxelQuad), &(meshData->opaqueQuads[0]), GL_STATIC_DRAW); canRender = true; if (!mesh.vaoID) buildVao(mesh); } else { if (mesh.vboID != 0) { glDeleteBuffers(1, &(mesh.vboID)); mesh.vboID = 0; } if (mesh.vaoID != 0) { glDeleteVertexArrays(1, &(mesh.vaoID)); mesh.vaoID = 0; } } if (meshData->transQuads.size()) { //vertex data mapBufferData(mesh.transVboID, meshData->transQuads.size() * sizeof(VoxelQuad), &(meshData->transQuads[0]), GL_STATIC_DRAW); //index data mapBufferData(mesh.transIndexID, mesh.transQuadIndices.size() * sizeof(ui32), &(mesh.transQuadIndices[0]), GL_STATIC_DRAW); canRender = true; mesh.needsSort = true; //must sort when changing the mesh if (!mesh.transVaoID) buildTransparentVao(mesh); } else { if (mesh.transVaoID != 0) { glDeleteVertexArrays(1, &(mesh.transVaoID)); mesh.transVaoID = 0; } if (mesh.transVboID != 0) { glDeleteBuffers(1, &(mesh.transVboID)); mesh.transVboID = 0; } if (mesh.transIndexID != 0) { glDeleteBuffers(1, &(mesh.transIndexID)); mesh.transIndexID = 0; } } if (meshData->cutoutQuads.size()) { mapBufferData(mesh.cutoutVboID, meshData->cutoutQuads.size() * sizeof(VoxelQuad), &(meshData->cutoutQuads[0]), GL_STATIC_DRAW); canRender = true; if (!mesh.cutoutVaoID) buildCutoutVao(mesh); } else { if (mesh.cutoutVaoID != 0) { glDeleteVertexArrays(1, &(mesh.cutoutVaoID)); mesh.cutoutVaoID = 0; } if (mesh.cutoutVboID != 0) { glDeleteBuffers(1, &(mesh.cutoutVboID)); mesh.cutoutVboID = 0; } } mesh.renderData = meshData->chunkMeshRenderData; //The missing break is deliberate! VORB_FALLTHROUGH; case MeshTaskType::LIQUID: mesh.renderData.waterIndexSize = meshData->chunkMeshRenderData.waterIndexSize; if (meshData->waterVertices.size()) { mapBufferData(mesh.waterVboID, meshData->waterVertices.size() * sizeof(LiquidVertex), &(meshData->waterVertices[0]), GL_STREAM_DRAW); canRender = true; if (!mesh.waterVaoID) buildWaterVao(mesh); } else { if (mesh.waterVboID != 0) { glDeleteBuffers(1, &(mesh.waterVboID)); mesh.waterVboID = 0; } if (mesh.waterVaoID != 0) { glDeleteVertexArrays(1, &(mesh.waterVaoID)); mesh.waterVaoID = 0; } } break; } return canRender; } void ChunkMesher::freeChunkMesh(CALLEE_DELETE ChunkMesh* mesh) { // Opaque if (mesh->vboID != 0) { glDeleteBuffers(1, &mesh->vboID); } if (mesh->vaoID != 0) { glDeleteVertexArrays(1, &mesh->vaoID); } // Transparent if (mesh->transVaoID != 0) { glDeleteVertexArrays(1, &mesh->transVaoID); } if (mesh->transVboID != 0) { glDeleteBuffers(1, &mesh->transVboID); } if (mesh->transIndexID != 0) { glDeleteBuffers(1, &mesh->transIndexID); } // Cutout if (mesh->cutoutVaoID != 0) { glDeleteVertexArrays(1, &mesh->cutoutVaoID); } if (mesh->cutoutVboID != 0) { glDeleteBuffers(1, &mesh->cutoutVboID); } // Liquid if (mesh->waterVboID != 0) { glDeleteBuffers(1, &mesh->waterVboID); } if (mesh->waterVaoID != 0) { glDeleteVertexArrays(1, &mesh->waterVaoID); } delete mesh; } // //CALLEE_DELETE ChunkMeshData* ChunkMesher::createOnlyWaterMesh(const Chunk* chunk) { // /*if (chunkMeshData != NULL) { // pError("Tried to create mesh with in use chunkMeshData!"); // return 0; // } // chunkMeshData = new ChunkMeshData(renderTask); // // _waterVboVerts.clear(); // // mi.task = renderTask; // // for (int i = 0; i < wSize; i++) { // mi.wc = m_wvec[i]; // mi.btype = GETBLOCKID(blockData[mi.wc]); // mi.x = (mi.wc % PADDED_CHUNK_WIDTH) - 1; // mi.y = (mi.wc / PADDED_CHUNK_LAYER) - 1; // mi.z = ((mi.wc % PADDED_CHUNK_LAYER) / PADDED_CHUNK_WIDTH) - 1; // // addLiquid(mi); // } // // // if (mi.liquidIndex) { // chunkMeshData->chunkMeshRenderData.waterIndexSize = (mi.liquidIndex * 6) / 4; // chunkMeshData->waterVertices.swap(_waterVboVerts); // }*/ // // return nullptr; //} void ChunkMesher::freeBuffers() { //free memory //std::vector ().swap(_vboVerts); //These dont get too big so it might be ok to not free them? /*vector ().swap(waterVboVerts); vector().swap(finalTopVerts); vector().swap(finalLeftVerts); vector().swap(finalRightVerts); vector().swap(finalFrontVerts); vector().swap(finalBackVerts); vector().swap(finalBottomVerts); vector().swap(finalNbVerts);*/ } #define CompareVertices(v1, v2) (!memcmp(&v1.color, &v2.color, 3) && v1.sunlight == v2.sunlight && !memcmp(&v1.lampColor, &v2.lampColor, 3) \ && !memcmp(&v1.overlayColor, &v2.overlayColor, 3) \ && v1.textureAtlas == v2.textureAtlas && v1.textureIndex == v2.textureIndex && v1.overlayTextureAtlas == v2.overlayTextureAtlas && v1.overlayTextureIndex == v2.overlayTextureIndex) #define CompareVerticesLight(v1, v2) (v1.sunlight == v2.sunlight && !memcmp(&v1.lampColor, &v2.lampColor, 3) && !memcmp(&v1.color, &v2.color, 3)) void ChunkMesher::addBlock() { // Ambient occlusion buffer for vertices f32 ao[4]; // Check the faces // Left if (shouldRenderFace(-1)) { computeAmbientOcclusion(-1, -PADDED_CHUNK_LAYER, PADDED_CHUNK_WIDTH, ao); addQuad(X_NEG, (int)vvox::Axis::Z, (int)vvox::Axis::Y, -PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, 2, ui8v2(1, 1), ao); } // Right if (shouldRenderFace(1)) { computeAmbientOcclusion(1, -PADDED_CHUNK_LAYER, -PADDED_CHUNK_WIDTH, ao); addQuad(X_POS, (int)vvox::Axis::Z, (int)vvox::Axis::Y, -PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, 0, ui8v2(-1, 1), ao); } // Bottom if (shouldRenderFace(-PADDED_CHUNK_LAYER)) { computeAmbientOcclusion(-PADDED_CHUNK_LAYER, PADDED_CHUNK_WIDTH, 1, ao); addQuad(Y_NEG, (int)vvox::Axis::X, (int)vvox::Axis::Z, -1, -PADDED_CHUNK_WIDTH, 2, ui8v2(1, 1), ao); } // Top if (shouldRenderFace(PADDED_CHUNK_LAYER)) { computeAmbientOcclusion(PADDED_CHUNK_LAYER, -PADDED_CHUNK_WIDTH, -1, ao); addQuad(Y_POS, (int)vvox::Axis::X, (int)vvox::Axis::Z, -1, -PADDED_CHUNK_WIDTH, 0, ui8v2(-1, 1), ao); } // Back if (shouldRenderFace(-PADDED_CHUNK_WIDTH)) { computeAmbientOcclusion(-PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, -1, ao); addQuad(Z_NEG, (int)vvox::Axis::X, (int)vvox::Axis::Y, -1, -PADDED_CHUNK_LAYER, 0, ui8v2(-1, 1), ao); } // Front if (shouldRenderFace(PADDED_CHUNK_WIDTH)) { computeAmbientOcclusion(PADDED_CHUNK_WIDTH, -PADDED_CHUNK_LAYER, 1, ao); addQuad(Z_POS, (int)vvox::Axis::X, (int)vvox::Axis::Y, -1, -PADDED_CHUNK_LAYER, 2, ui8v2(1, 1), ao); } } void ChunkMesher::computeAmbientOcclusion(int upOffset VORB_UNUSED, int frontOffset VORB_UNUSED, int rightOffset VORB_UNUSED, f32 ambientOcclusion VORB_UNUSED[]) { #ifdef USE_AO // Ambient occlusion factor #define OCCLUSION_FACTOR 0.2f; // Helper macro // TODO(Ben): This isn't exactly right since self will occlude. Use a function #define CALCULATE_VERTEX(v, s1, s2) \ nearOccluders = getOcclusion(blocks->operator[](blockData[blockIndex])) + \ getOcclusion(blocks->operator[](blockData[blockIndex s1 frontOffset])) + \ getOcclusion(blocks->operator[](blockData[blockIndex s2 rightOffset])) + \ getOcclusion(blocks->operator[](blockData[blockIndex s1 frontOffset s2 rightOffset])); \ ambientOcclusion[v] = 1.0f - nearOccluders * OCCLUSION_FACTOR; // Move the block index upwards int blockIndex = this->blockIndex + upOffset; int nearOccluders; ///< For ambient occlusion // TODO(Ben): I know for a fact the inputs are wrong for some faces // Vertex 0 CALCULATE_VERTEX(0, -, -) // Vertex 1 CALCULATE_VERTEX(1, +, -) // Vertex 2 CALCULATE_VERTEX(2, +, +) // Vertex 3 CALCULATE_VERTEX(3, -, +) #endif } void ChunkMesher::addQuad(int face, int rightAxis, int frontAxis, int leftOffset, int backOffset, int rightStretchIndex, const ui8v2& texOffset, f32 ambientOcclusion VORB_UNUSED[]) { // Get texture TODO(Ben): Null check? const BlockTexture* texture = block->textures[face]; // Get colors // TODO(Ben): altColors color3 blockColor[2]; texture->layers.base.getFinalColor(blockColor[B_INDEX], heightData->temperature, heightData->humidity, 0); texture->layers.base.getFinalColor(blockColor[O_INDEX], heightData->temperature, heightData->humidity, 0); std::vector& quads = m_quads[face]; // Get texturing parameters ui8 blendMode = getBlendMode(texture->blendMode); // TODO(Ben): Make this better BlockTextureMethodData methodDatas[6]; texture->layers.base.getBlockTextureMethodData(m_textureMethodParams[face][B_INDEX], blockColor[B_INDEX], methodDatas[0]); texture->layers.base.getNormalTextureMethodData(m_textureMethodParams[face][B_INDEX], blockColor[B_INDEX], methodDatas[1]); texture->layers.base.getDispTextureMethodData(m_textureMethodParams[face][B_INDEX], blockColor[B_INDEX], methodDatas[2]); texture->layers.overlay.getBlockTextureMethodData(m_textureMethodParams[face][O_INDEX], blockColor[O_INDEX], methodDatas[3]); texture->layers.overlay.getNormalTextureMethodData(m_textureMethodParams[face][O_INDEX], blockColor[O_INDEX], methodDatas[4]); texture->layers.overlay.getDispTextureMethodData(m_textureMethodParams[face][O_INDEX], blockColor[O_INDEX], methodDatas[5]); ui8 atlasIndices[6]; for (int i = 0; i < 6; i++) { atlasIndices[i] = (ui8)(methodDatas[i].index / ATLAS_SIZE); methodDatas[i].index &= ATLAS_MODULUS_BITS; } i32v3 pos(bx, by, bz); ui8 uOffset = (ui8)(pos[FACE_AXIS[face][0]] * FACE_AXIS_SIGN[face][0]); ui8 vOffset = (ui8)(pos[FACE_AXIS[face][1]] * FACE_AXIS_SIGN[face][1]); // Construct the quad // i16 quadIndex = quads.size(); quads.emplace_back(); m_numQuads++; VoxelQuad* quad = &quads.back(); quad->v.v0.mesherFlags = MESH_FLAG_ACTIVE; for (int i = 0; i < 4; i++) { BlockVertex& v = quad->verts[i]; v.position = VoxelMesher::VOXEL_POSITIONS[face][i] + voxelPosOffset; #ifdef USE_AO f32& ao = ambientOcclusion[i]; v.color.r = (ui8)(blockColor[B_INDEX].r * ao); v.color.g = (ui8)(blockColor[B_INDEX].g * ao); v.color.b = (ui8)(blockColor[B_INDEX].b * ao); v.overlayColor.r = (ui8)(blockColor[O_INDEX].r * ao); v.overlayColor.g = (ui8)(blockColor[O_INDEX].g * ao); v.overlayColor.b = (ui8)(blockColor[O_INDEX].b * ao); #else v.color = blockColor[B_INDEX]; v.overlayColor = blockColor[O_INDEX]; #endif // TODO(Ben) array? v.texturePosition.base.index = (ui8)methodDatas[0].index; v.texturePosition.base.atlas = atlasIndices[0]; v.normTexturePosition.base.index = (ui8)methodDatas[1].index; v.normTexturePosition.base.atlas = atlasIndices[1]; v.dispTexturePosition.base.index = (ui8)methodDatas[2].index; v.dispTexturePosition.base.atlas = atlasIndices[2]; v.texturePosition.overlay.index = (ui8)methodDatas[3].index; v.texturePosition.overlay.atlas = atlasIndices[3]; v.normTexturePosition.overlay.index = (ui8)methodDatas[4].index; v.normTexturePosition.overlay.atlas = atlasIndices[4]; v.dispTexturePosition.overlay.index = (ui8)methodDatas[5].index; v.dispTexturePosition.overlay.atlas = atlasIndices[5]; v.textureDims = methodDatas[0].size; v.overlayTextureDims = methodDatas[3].size; v.blendMode = blendMode; v.face = (ui8)face; } // Set texture coordinates quad->verts[0].tex.x = (ui8)(UV_0 + uOffset); quad->verts[0].tex.y = (ui8)(UV_1 + vOffset); quad->verts[1].tex.x = (ui8)(UV_0 + uOffset); quad->verts[1].tex.y = (ui8)(UV_0 + vOffset); quad->verts[2].tex.x = (ui8)(UV_1 + uOffset); quad->verts[2].tex.y = (ui8)(UV_0 + vOffset); quad->verts[3].tex.x = (ui8)(UV_1 + uOffset); quad->verts[3].tex.y = (ui8)(UV_1 + vOffset); // Check against lowest and highest for culling in render // TODO(Ben): Think about this more if (quad->v.v0.position.x < m_lowestX) m_lowestX = quad->v.v0.position.x; if (quad->v.v0.position.x > m_highestX) m_highestX = quad->v.v0.position.x; if (quad->v.v0.position.y < m_lowestY) m_lowestY = quad->v.v0.position.y; if (quad->v.v0.position.y > m_highestY) m_highestY = quad->v.v0.position.y; if (quad->v.v0.position.z < m_lowestZ) m_lowestZ = quad->v.v0.position.z; if (quad->v.v0.position.z > m_highestZ) m_highestZ = quad->v.v0.position.z; m_numQuads -= tryMergeQuad(quad, quads, face, rightAxis, frontAxis, leftOffset, backOffset, rightStretchIndex, texOffset); } struct FloraQuadData { color3 blockColor[2]; BlockTextureMethodData methodDatas[6]; ui8 atlasIndices[6]; ui8 blendMode; ui8 uOffset; ui8 vOffset; const BlockTexture* texture; }; //adds a flora mesh void ChunkMesher::addFlora() { FloraQuadData data; data.texture = block->textures[0]; // Get colors // TODO(Ben): altColors data.texture->layers.base.getFinalColor(data.blockColor[B_INDEX], heightData->temperature, heightData->humidity, 0); data.texture->layers.base.getFinalColor(data.blockColor[O_INDEX], heightData->temperature, heightData->humidity, 0); // Get texturing parameters data.blendMode = getBlendMode(data.texture->blendMode); data.texture->layers.base.getBlockTextureMethodData(m_textureMethodParams[0][B_INDEX], data.blockColor[B_INDEX], data.methodDatas[0]); data.texture->layers.base.getNormalTextureMethodData(m_textureMethodParams[0][B_INDEX], data.blockColor[B_INDEX], data.methodDatas[1]); data.texture->layers.base.getDispTextureMethodData(m_textureMethodParams[0][B_INDEX], data.blockColor[B_INDEX], data.methodDatas[2]); data.texture->layers.overlay.getBlockTextureMethodData(m_textureMethodParams[0][O_INDEX], data.blockColor[O_INDEX], data.methodDatas[3]); data.texture->layers.overlay.getNormalTextureMethodData(m_textureMethodParams[0][O_INDEX], data.blockColor[O_INDEX], data.methodDatas[4]); data.texture->layers.overlay.getDispTextureMethodData(m_textureMethodParams[0][O_INDEX], data.blockColor[O_INDEX], data.methodDatas[5]); for (int i = 0; i < 6; i++) { data.atlasIndices[i] = (ui8)(data.methodDatas[i].index / ATLAS_SIZE); data.methodDatas[i].index &= ATLAS_MODULUS_BITS; } i32v3 pos(bx, by, bz); data.uOffset = (ui8)(pos[FACE_AXIS[0][0]] * FACE_AXIS_SIGN[0][0]); data.vOffset = (ui8)(pos[FACE_AXIS[0][1]] * FACE_AXIS_SIGN[0][1]); int r; switch (block->meshType) { case MeshType::LEAVES: break; case MeshType::CROSSFLORA: //Generate a random number between 0 and 3 inclusive r = std::bind(std::uniform_int_distribution(0, NUM_CROSSFLORA_MESHES-1), std::mt19937(/*getPositionSeed(mi.nx, mi.nz)*/))(); ChunkMesher::addFloraQuad(VoxelMesher::crossFloraVertices[r], data); ChunkMesher::addFloraQuad(VoxelMesher::crossFloraVertices[r] + 4, data); break; case MeshType::TRIANGLE: //Generate a random number between 0 and 3 inclusive r = std::bind(std::uniform_int_distribution(0, NUM_FLORA_MESHES-1), std::mt19937(/*getPositionSeed(mi.nx, mi.nz)*/))(); ChunkMesher::addFloraQuad(VoxelMesher::floraVertices[r], data); ChunkMesher::addFloraQuad(VoxelMesher::floraVertices[r] + 4, data); ChunkMesher::addFloraQuad(VoxelMesher::floraVertices[r] + 8, data); break; default: break; } } void ChunkMesher::addFloraQuad(const ui8v3* positions, FloraQuadData& data) { m_floraQuads.emplace_back(); VoxelQuad& quad = m_floraQuads.back(); for (int i = 0; i < 4; i++) { BlockVertex& v = quad.verts[i]; v.position = positions[i] + voxelPosOffset; v.color = data.blockColor[B_INDEX]; v.overlayColor = data.blockColor[O_INDEX]; // TODO(Ben) array? v.texturePosition.base.index = (ui8)data.methodDatas[0].index; v.texturePosition.base.atlas = data.atlasIndices[0]; v.normTexturePosition.base.index = (ui8)data.methodDatas[1].index; v.normTexturePosition.base.atlas = data.atlasIndices[1]; v.dispTexturePosition.base.index = (ui8)data.methodDatas[2].index; v.dispTexturePosition.base.atlas = data.atlasIndices[2]; v.texturePosition.overlay.index = (ui8)data.methodDatas[3].index; v.texturePosition.overlay.atlas = data.atlasIndices[3]; v.normTexturePosition.overlay.index = (ui8)data.methodDatas[4].index; v.normTexturePosition.overlay.atlas = data.atlasIndices[4]; v.dispTexturePosition.overlay.index = (ui8)data.methodDatas[5].index; v.dispTexturePosition.overlay.atlas = data.atlasIndices[5]; v.textureDims = data.methodDatas[0].size; v.overlayTextureDims = data.methodDatas[3].size; v.blendMode = data.blendMode; v.face = (ui8)vvox::Cardinal::Y_POS; } // Set texture coordinates quad.verts[0].tex.x = (ui8)(UV_0 + data.uOffset); quad.verts[0].tex.y = (ui8)(UV_1 + data.vOffset); quad.verts[1].tex.x = (ui8)(UV_0 + data.uOffset); quad.verts[1].tex.y = (ui8)(UV_0 + data.vOffset); quad.verts[2].tex.x = (ui8)(UV_1 + data.uOffset); quad.verts[2].tex.y = (ui8)(UV_0 + data.vOffset); quad.verts[3].tex.x = (ui8)(UV_1 + data.uOffset); quad.verts[3].tex.y = (ui8)(UV_1 + data.vOffset); // Check against lowest and highest for culling in render // TODO(Ben): Think about this more if (quad.v.v0.position.x < m_lowestX) m_lowestX = quad.v.v0.position.x; if (quad.v.v0.position.x > m_highestX) m_highestX = quad.v.v0.position.x; if (quad.v.v0.position.y < m_lowestY) m_lowestY = quad.v.v0.position.y; if (quad.v.v0.position.y > m_highestY) m_highestY = quad.v.v0.position.y; if (quad.v.v0.position.z < m_lowestZ) m_lowestZ = quad.v.v0.position.z; if (quad.v.v0.position.z > m_highestZ) m_highestZ = quad.v.v0.position.z; } int ChunkMesher::tryMergeQuad(VoxelQuad* quad, std::vector& quads, int face, int rightAxis, int frontAxis, int leftOffset, int backOffset, int rightStretchIndex, const ui8v2& texOffset) { int rv = 0; i16 quadIndex = quads.size() - 1; if (quad->v.v0 == quad->v.v3 && quad->v.v1 == quad->v.v2) { quad->v.v0.mesherFlags |= MESH_FLAG_MERGE_RIGHT; ui16 leftIndex = m_quadIndices[blockIndex + leftOffset][face]; // Check left merge if (leftIndex != NO_QUAD_INDEX) { VoxelQuad& lQuad = quads[leftIndex]; if (((lQuad.v.v0.mesherFlags & MESH_FLAG_MERGE_RIGHT) != 0) && lQuad.v.v0.position[frontAxis] == quad->v.v0.position[frontAxis] && lQuad.v.v1.position[frontAxis] == quad->v.v1.position[frontAxis] && lQuad.v.v0 == quad->v.v0 && lQuad.v.v1 == quad->v.v1) { // Stretch the previous quad lQuad.verts[rightStretchIndex].position[rightAxis] += QUAD_SIZE; lQuad.verts[rightStretchIndex].tex.x += texOffset.x; lQuad.verts[rightStretchIndex + 1].position[rightAxis] += QUAD_SIZE; lQuad.verts[rightStretchIndex + 1].tex.x += texOffset.x; // Remove the current quad quads.pop_back(); ++rv; quadIndex = leftIndex; quad = &lQuad; } } } // Check back merge if (quad->v.v0 == quad->v.v1 && quad->v.v2 == quad->v.v3) { quad->v.v0.mesherFlags |= MESH_FLAG_MERGE_FRONT; int backIndex = m_quadIndices[blockIndex + backOffset][face]; if (backIndex != NO_QUAD_INDEX) { VoxelQuad* bQuad = &quads[backIndex]; while (!(bQuad->v.v0.mesherFlags & MESH_FLAG_ACTIVE)) { backIndex = bQuad->v.replaceQuad; bQuad = &quads[backIndex]; } if (((bQuad->v.v0.mesherFlags & MESH_FLAG_MERGE_FRONT) != 0) && bQuad->v.v0.position[rightAxis] == quad->v.v0.position[rightAxis] && bQuad->v.v2.position[rightAxis] == quad->v.v2.position[rightAxis] && bQuad->v.v0 == quad->v.v0 && bQuad->v.v1 == quad->v.v1) { bQuad->v.v0.position[frontAxis] += QUAD_SIZE; bQuad->v.v0.tex.y += texOffset.y; bQuad->v.v3.position[frontAxis] += QUAD_SIZE; bQuad->v.v3.tex.y += texOffset.y; quadIndex = backIndex; // Mark as not in use quad->v.v0.mesherFlags = 0; quad->v.replaceQuad = backIndex; ++rv; } } } // Mark quadIndices so we can merge this quad later m_quadIndices[blockIndex][face] = quadIndex; // Return number of merges return rv; } //Gets the liquid level from a block index #define LEVEL(i) ((_blockIDData[i] == 0) ? 0 : (((nextBlock = &GETBLOCK(_blockIDData[i]))->caIndex == block.caIndex) ? nextBlock->waterMeshLevel : 0)) #define CALCULATE_LIQUID_VERTEX_HEIGHT(height, heightA, heightB, cornerIndex) \ div = 0; \ tot = 0; \ if (heightA) { \ tot += heightA; \ div++; \ } \ \ if (heightB) { \ tot += heightB; \ div++; \ } \ \ if (div) { \ int lvl = LEVEL(cornerIndex); \ if (lvl) { \ tot += lvl; \ div++; \ } \ height = (height + (tot / (float)maxLevel)) / (div + 1); \ } //END CALCULATE_LIQUID_VERTEX_HEIGHT // TODO(Ben): Instead of 8 verts per box, share vertices and index into it! void ChunkMesher::addLiquid() { // const Block &block = (*m_blocks)[mi.btype]; // Block* nextBlock; // i32 nextBlockID; // const i32 wc = mi.wc; // i32 x = mi.x; // i32 y = mi.y; // i32 z = mi.z; // RenderTask* task = mi.task; // const i32 maxLevel = 100; // float liquidLevel = block.waterMeshLevel / (float)maxLevel; // float fallingReduction = 0.0f; // bool faces[6] = { false, false, false, false, false, false }; // float backLeftHeight = liquidLevel; // float backRightHeight = liquidLevel; // float frontRightHeight = liquidLevel; // float frontLeftHeight = liquidLevel; // const i32 MIN_ALPHA = 75; // const i32 MAX_ALPHA = 175; // const i32 ALPHA_RANGE = MAX_ALPHA - MIN_ALPHA; // ui8 backRightAlpha, frontRightAlpha, frontLeftAlpha, backLeftAlpha; // i32 textureUnit = 0; // i32 div; // i32 tot; // i32 left, right, back, front, bottom; // ui8 uOff = x * 7; // ui8 vOff = 224 - z * 7; // ui8 temperature = chunkGridData->heightData[x + z*CHUNK_WIDTH].temperature; // ui8 depth = chunkGridData->heightData[x + z*CHUNK_WIDTH].depth; // ColorRGB8 color;// = GameManager::texturePackLoader->getColorMap(TerrainGenerator::DefaultColorMaps::WATER)[depth * 256 + temperature]; // ui8 sunlight = _sunlightData[wc]; // ColorRGB8 lampLight((_lampLightData[wc] & LAMP_RED_MASK) >> LAMP_RED_SHIFT, // (_lampLightData[wc] & LAMP_GREEN_MASK) >> LAMP_GREEN_SHIFT, // _lampLightData[wc] & LAMP_BLUE_MASK); // sunlight = (ui8)(255.0f*(LIGHT_OFFSET + pow(LIGHT_MULT, MAXLIGHT - sunlight))); // lampLight.r = (ui8)(255.0f*(LIGHT_OFFSET + pow(LIGHT_MULT, MAXLIGHT - lampLight.r))); // lampLight.g = (ui8)(255.0f*(LIGHT_OFFSET + pow(LIGHT_MULT, MAXLIGHT - lampLight.g))); // lampLight.b = (ui8)(255.0f*(LIGHT_OFFSET + pow(LIGHT_MULT, MAXLIGHT - lampLight.b))); // nextBlockID = _blockIDData[wc + PADDED_OFFSETS::BOTTOM]; // nextBlock = &GETBLOCK(nextBlockID); // //Check if the block is falling // if (nextBlockID == 0 || nextBlock->waterBreak || (nextBlock->caIndex == block.caIndex && nextBlock->waterMeshLevel != maxLevel)) { // memset(faces, 1, 6); //all faces are active //// backLeftHeight = backRightHeight = frontLeftHeight = frontRightHeight // fallingReduction = 1.0f; // } else { // //Get occlusion // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::LEFT]); // faces[XNEG] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::BACK]); // faces[ZNEG] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::RIGHT]); // faces[XPOS] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::FRONT]); // faces[ZPOS] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::BOTTOM]); // faces[YNEG] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // } // left = LEVEL(wc + PADDED_OFFSETS::LEFT); // right = LEVEL(wc + PADDED_OFFSETS::RIGHT); // back = LEVEL(wc + PADDED_OFFSETS::BACK); // front = LEVEL(wc + PADDED_OFFSETS::FRONT); // bottom = LEVEL(wc + PADDED_OFFSETS::BOTTOM); // //Calculate the liquid levels // //Back Left Vertex // CALCULATE_LIQUID_VERTEX_HEIGHT(backLeftHeight, left, back, wc + PADDED_OFFSETS::BACK_LEFT); // //Back Right Vertex // CALCULATE_LIQUID_VERTEX_HEIGHT(backRightHeight, right, back, wc + PADDED_OFFSETS::BACK_RIGHT); // //Front Right Vertex // CALCULATE_LIQUID_VERTEX_HEIGHT(frontRightHeight, right, front, wc + PADDED_OFFSETS::FRONT_RIGHT); // //Front Left Vertex // CALCULATE_LIQUID_VERTEX_HEIGHT(frontLeftHeight, left, front, wc + PADDED_OFFSETS::FRONT_LEFT); // //only occlude top if we are a full water block and our sides arent down at all // if (liquidLevel == 1.0f && backRightHeight == 1.0f && backLeftHeight == 1.0f && frontLeftHeight == 1.0f && frontRightHeight == 1.0f) { // nextBlock = &GETBLOCK(_blockIDData[wc + PADDED_OFFSETS::TOP]); // faces[YPOS] = ((nextBlock->caIndex != block.caIndex) && (nextBlock->occlude == BlockOcclusion::NONE)); // } else { // faces[YPOS] = true; // } // // //Compute alpha // if (bottom == maxLevel) { // backRightAlpha = backLeftAlpha = frontRightAlpha = frontLeftAlpha = MAX_ALPHA; // } else { // backRightAlpha = (ui8)(backRightHeight * ALPHA_RANGE + MIN_ALPHA); // backLeftAlpha = (ui8)(backLeftHeight * ALPHA_RANGE + MIN_ALPHA); // frontRightAlpha = (ui8)(frontRightHeight * ALPHA_RANGE + MIN_ALPHA); // frontLeftAlpha = (ui8)(frontLeftHeight * ALPHA_RANGE + MIN_ALPHA); // } // //Add vertices for the faces // if (faces[YNEG]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // // _waterVboVerts[mi.liquidIndex].position[0] = x + VoxelMesher::liquidVertices[48]; // _waterVboVerts[mi.liquidIndex].position[1] = y + VoxelMesher::liquidVertices[49] - fallingReduction; // _waterVboVerts[mi.liquidIndex].position[2] = z + VoxelMesher::liquidVertices[50]; // _waterVboVerts[mi.liquidIndex + 1].position[0] = x + VoxelMesher::liquidVertices[51]; // _waterVboVerts[mi.liquidIndex + 1].position[1] = y + VoxelMesher::liquidVertices[52] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 1].position[2] = z + VoxelMesher::liquidVertices[53]; // _waterVboVerts[mi.liquidIndex + 2].position[0] = x + VoxelMesher::liquidVertices[54]; // _waterVboVerts[mi.liquidIndex + 2].position[1] = y + VoxelMesher::liquidVertices[55] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 2].position[2] = z + VoxelMesher::liquidVertices[56]; // _waterVboVerts[mi.liquidIndex + 3].position[0] = x + VoxelMesher::liquidVertices[57]; // _waterVboVerts[mi.liquidIndex + 3].position[1] = y + VoxelMesher::liquidVertices[58] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 3].position[2] = z + VoxelMesher::liquidVertices[59]; // //set alpha // _waterVboVerts[mi.liquidIndex].color.a = backLeftAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = backRightAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = frontRightAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = backLeftAlpha; // mi.liquidIndex += 4; // } // if (faces[ZPOS]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // _waterVboVerts[mi.liquidIndex].position[0] = x + VoxelMesher::liquidVertices[0]; // _waterVboVerts[mi.liquidIndex].position[1] = y + frontLeftHeight; // _waterVboVerts[mi.liquidIndex].position[2] = z + VoxelMesher::liquidVertices[2]; // _waterVboVerts[mi.liquidIndex + 1].position[0] = x + VoxelMesher::liquidVertices[3]; // _waterVboVerts[mi.liquidIndex + 1].position[1] = y + VoxelMesher::liquidVertices[4] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 1].position[2] = z + VoxelMesher::liquidVertices[5]; // _waterVboVerts[mi.liquidIndex + 2].position[0] = x + VoxelMesher::liquidVertices[6]; // _waterVboVerts[mi.liquidIndex + 2].position[1] = y + VoxelMesher::liquidVertices[7] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 2].position[2] = z + VoxelMesher::liquidVertices[8]; // _waterVboVerts[mi.liquidIndex + 3].position[0] = x + VoxelMesher::liquidVertices[9]; // _waterVboVerts[mi.liquidIndex + 3].position[1] = y + frontRightHeight; // _waterVboVerts[mi.liquidIndex + 3].position[2] = z + VoxelMesher::liquidVertices[11]; // _waterVboVerts[mi.liquidIndex].color.a = frontLeftAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = frontLeftAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = frontRightAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = frontRightAlpha; // mi.liquidIndex += 4; // } // if (faces[YPOS]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // _waterVboVerts.resize(_waterVboVerts.size() + 4); // _waterVboVerts[mi.liquidIndex].position[0] = x + VoxelMesher::liquidVertices[24]; // _waterVboVerts[mi.liquidIndex].position[1] = y + backLeftHeight; // _waterVboVerts[mi.liquidIndex].position[2] = z + VoxelMesher::liquidVertices[26]; // _waterVboVerts[mi.liquidIndex + 1].position[0] = x + VoxelMesher::liquidVertices[27]; // _waterVboVerts[mi.liquidIndex + 1].position[1] = y + frontLeftHeight; // _waterVboVerts[mi.liquidIndex + 1].position[2] = z + VoxelMesher::liquidVertices[29]; // _waterVboVerts[mi.liquidIndex + 2].position[0] = x + VoxelMesher::liquidVertices[30]; // _waterVboVerts[mi.liquidIndex + 2].position[1] = y + frontRightHeight; // _waterVboVerts[mi.liquidIndex + 2].position[2] = z + VoxelMesher::liquidVertices[32]; // _waterVboVerts[mi.liquidIndex + 3].position[0] = x + VoxelMesher::liquidVertices[33]; // _waterVboVerts[mi.liquidIndex + 3].position[1] = y + backRightHeight; // _waterVboVerts[mi.liquidIndex + 3].position[2] = z + VoxelMesher::liquidVertices[35]; // _waterVboVerts[mi.liquidIndex].color.a = backLeftAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = frontLeftAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = frontRightAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = backRightAlpha; // mi.liquidIndex += 4; // } // if (faces[ZNEG]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // _waterVboVerts[mi.liquidIndex].position[0] = x + VoxelMesher::liquidVertices[60]; // _waterVboVerts[mi.liquidIndex].position[1] = y + backRightHeight; // _waterVboVerts[mi.liquidIndex].position[2] = z + VoxelMesher::liquidVertices[62]; // _waterVboVerts[mi.liquidIndex + 1].position[0] = x + VoxelMesher::liquidVertices[63]; // _waterVboVerts[mi.liquidIndex + 1].position[1] = y + VoxelMesher::liquidVertices[64] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 1].position[2] = z + VoxelMesher::liquidVertices[65]; // _waterVboVerts[mi.liquidIndex + 2].position[0] = x + VoxelMesher::liquidVertices[66]; // _waterVboVerts[mi.liquidIndex + 2].position[1] = y + VoxelMesher::liquidVertices[67] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 2].position[2] = z + VoxelMesher::liquidVertices[68]; // _waterVboVerts[mi.liquidIndex + 3].position[0] = x + VoxelMesher::liquidVertices[69]; // _waterVboVerts[mi.liquidIndex + 3].position[1] = y + backLeftHeight; // _waterVboVerts[mi.liquidIndex + 3].position[2] = z + VoxelMesher::liquidVertices[71]; // _waterVboVerts[mi.liquidIndex].color.a = backRightAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = backRightAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = backLeftAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = backLeftAlpha; // mi.liquidIndex += 4; // } // if (faces[XPOS]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // _waterVboVerts[mi.liquidIndex].position.x = x + VoxelMesher::liquidVertices[12]; // _waterVboVerts[mi.liquidIndex].position.y = y + frontRightHeight; // _waterVboVerts[mi.liquidIndex].position.z = z + VoxelMesher::liquidVertices[14]; // _waterVboVerts[mi.liquidIndex + 1].position.x = x + VoxelMesher::liquidVertices[15]; // _waterVboVerts[mi.liquidIndex + 1].position.y = y + VoxelMesher::liquidVertices[16] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 1].position.z = z + VoxelMesher::liquidVertices[17]; // _waterVboVerts[mi.liquidIndex + 2].position.x = x + VoxelMesher::liquidVertices[18]; // _waterVboVerts[mi.liquidIndex + 2].position.y = y + VoxelMesher::liquidVertices[19] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 2].position.z = z + VoxelMesher::liquidVertices[20]; // _waterVboVerts[mi.liquidIndex + 3].position.x = x + VoxelMesher::liquidVertices[21]; // _waterVboVerts[mi.liquidIndex + 3].position.y = y + backRightHeight; // _waterVboVerts[mi.liquidIndex + 3].position.z = z + VoxelMesher::liquidVertices[23]; // _waterVboVerts[mi.liquidIndex].color.a = frontRightAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = frontRightAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = backRightAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = backRightAlpha; // mi.liquidIndex += 4; // } // if (faces[XNEG]){ // VoxelMesher::makeLiquidFace(_waterVboVerts, mi.liquidIndex, uOff, vOff, lampLight, sunlight, color, textureUnit); // _waterVboVerts[mi.liquidIndex].position[0] = x + VoxelMesher::liquidVertices[36]; // _waterVboVerts[mi.liquidIndex].position[1] = y + backLeftHeight; // _waterVboVerts[mi.liquidIndex].position[2] = z + VoxelMesher::liquidVertices[38]; // _waterVboVerts[mi.liquidIndex + 1].position[0] = x + VoxelMesher::liquidVertices[39]; // _waterVboVerts[mi.liquidIndex + 1].position[1] = y + VoxelMesher::liquidVertices[40] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 1].position[2] = z + VoxelMesher::liquidVertices[41]; // _waterVboVerts[mi.liquidIndex + 2].position[0] = x + VoxelMesher::liquidVertices[42]; // _waterVboVerts[mi.liquidIndex + 2].position[1] = y + VoxelMesher::liquidVertices[43] - fallingReduction; // _waterVboVerts[mi.liquidIndex + 2].position[2] = z + VoxelMesher::liquidVertices[44]; // _waterVboVerts[mi.liquidIndex + 3].position[0] = x + VoxelMesher::liquidVertices[45]; // _waterVboVerts[mi.liquidIndex + 3].position[1] = y + frontLeftHeight; // _waterVboVerts[mi.liquidIndex + 3].position[2] = z + VoxelMesher::liquidVertices[47]; // _waterVboVerts[mi.liquidIndex].color.a = backLeftAlpha; // _waterVboVerts[mi.liquidIndex + 1].color.a = backLeftAlpha; // _waterVboVerts[mi.liquidIndex + 2].color.a = frontLeftAlpha; // _waterVboVerts[mi.liquidIndex + 3].color.a = frontLeftAlpha; // mi.liquidIndex += 4; // } } int ChunkMesher::getLiquidLevel(int blockIndex, const Block& block) { int val = GETBLOCKID(blockData[blockIndex]); // Get block ID val = val - block.liquidStartID; if (val < 0) return 0; if (val > block.liquidLevels) return 0; return val; } bool ChunkMesher::shouldRenderFace(int offset) { const Block& neighbor = blocks->operator[](blockData[blockIndex + offset]); if (neighbor.occlude == BlockOcclusion::ALL) return false; if ((neighbor.occlude == BlockOcclusion::SELF) && (blockID == neighbor.ID)) return false; return true; } int ChunkMesher::getOcclusion(const Block& block) { if (block.occlude == BlockOcclusion::ALL) return 1; if ((block.occlude == BlockOcclusion::SELF) && (blockID == block.ID)) return 1; return 0; } ui8 ChunkMesher::getBlendMode(const BlendType& blendType) { // Shader interprets this with bitwise ops ubyte blendMode = 0x14; //0x14 = 00 01 01 00 switch (blendType) { case BlendType::ALPHA: blendMode |= 1; //Sets blendMode to 00 01 01 01 break; case BlendType::ADD: blendMode += 4; //Sets blendMode to 00 01 10 00 break; case BlendType::SUBTRACT: blendMode -= 4; //Sets blendMode to 00 01 00 00 break; case BlendType::MULTIPLY: blendMode -= 16; //Sets blendMode to 00 00 01 00 break; } return blendMode; } void ChunkMesher::buildTransparentVao(ChunkMesh& cm) { glGenVertexArrays(1, &(cm.transVaoID)); glBindVertexArray(cm.transVaoID); glBindBuffer(GL_ARRAY_BUFFER, cm.transVboID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm.transIndexID); for (int i = 0; i < 8; i++) { glEnableVertexAttribArray(i); } // TODO(Ben): Might be wrong // vPosition_Face glVertexAttribPointer(0, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, position)); // vTex_Animation_BlendMode glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, tex)); // vTexturePos glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, texturePosition)); // vNormTexturePos glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, normTexturePosition)); // vDispTexturePos glVertexAttribPointer(4, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, dispTexturePosition)); // vTexDims glVertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, textureDims)); // vColor glVertexAttribPointer(6, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, color)); // vOverlayColor glVertexAttribPointer(7, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, overlayColor)); glBindVertexArray(0); } void ChunkMesher::buildCutoutVao(ChunkMesh& cm) { glGenVertexArrays(1, &(cm.cutoutVaoID)); glBindVertexArray(cm.cutoutVaoID); glBindBuffer(GL_ARRAY_BUFFER, cm.cutoutVboID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ChunkRenderer::sharedIBO); for (int i = 0; i < 8; i++) { glEnableVertexAttribArray(i); } // vPosition_Face glVertexAttribPointer(0, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, position)); // vTex_Animation_BlendMode glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, tex)); // vTexturePos glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, texturePosition)); // vNormTexturePos glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, normTexturePosition)); // vDispTexturePos glVertexAttribPointer(4, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, dispTexturePosition)); // vTexDims glVertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, textureDims)); // vColor glVertexAttribPointer(6, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, color)); // vOverlayColor glVertexAttribPointer(7, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, overlayColor)); glBindVertexArray(0); } void ChunkMesher::buildVao(ChunkMesh& cm) { glGenVertexArrays(1, &(cm.vaoID)); glBindVertexArray(cm.vaoID); glBindBuffer(GL_ARRAY_BUFFER, cm.vboID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ChunkRenderer::sharedIBO); for (int i = 0; i < 8; i++) { glEnableVertexAttribArray(i); } // vPosition_Face glVertexAttribPointer(0, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, position)); // vTex_Animation_BlendMode glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, tex)); // vTexturePos glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, texturePosition)); // vNormTexturePos glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, normTexturePosition)); // vDispTexturePos glVertexAttribPointer(4, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, dispTexturePosition)); // vTexDims glVertexAttribPointer(5, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(BlockVertex), offsetptr(BlockVertex, textureDims)); // vColor glVertexAttribPointer(6, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, color)); // vOverlayColor glVertexAttribPointer(7, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(BlockVertex), offsetptr(BlockVertex, overlayColor)); glBindVertexArray(0); } void ChunkMesher::buildWaterVao(ChunkMesh& cm) { glGenVertexArrays(1, &(cm.waterVaoID)); glBindVertexArray(cm.waterVaoID); glBindBuffer(GL_ARRAY_BUFFER, cm.waterVboID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ChunkRenderer::sharedIBO); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); glBindBuffer(GL_ARRAY_BUFFER, cm.waterVboID); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(LiquidVertex), 0); //uvs_texUnit_texIndex glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(LiquidVertex), (char *)12); //color glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(LiquidVertex), (char *)16); //light glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(LiquidVertex), (char *)20); glBindVertexArray(0); } ================================================ FILE: SoA/ChunkMesher.h ================================================ #pragma once #include "Vertex.h" #include "BlockData.h" #include "Chunk.h" #include "ChunkMesh.h" #include "ChunkMeshTask.h" class BlockPack; class BlockTextureLayer; class ChunkMeshData; struct BlockTexture; struct PlanetHeightData; struct FloraQuadData; // Sizes For A Padded Chunk const int PADDED_CHUNK_WIDTH = (CHUNK_WIDTH + 2); const int PADDED_CHUNK_LAYER = (PADDED_CHUNK_WIDTH * PADDED_CHUNK_WIDTH); const int PADDED_CHUNK_SIZE = (PADDED_CHUNK_LAYER * PADDED_CHUNK_WIDTH); // !!! IMPORTANT !!! // TODO(BEN): Make a class for complex Chunk Mesh Splicing. Store plenty of metadata in RAM about the regions in each mesh and just do a CPU copy to align them all and mix them around. Then meshes can be remeshed, rendered, recombined, at will. // Requirements: Each chunk is only meshed when it needs to, as they do now. // Rather than remeshing when combining and splitting, we just download the data from the GPU (or cache it compressed in RAM on 64 bit systems?) // Copy it around, and re-upload. // glBufferSubData and maybe even re-use buffers to minimize bandwidth??? // each worker thread gets one of these // This class is too big to statically allocate class ChunkMesher { public: ChunkMesher():blocks(nullptr), m_chunkMeshData(nullptr){} void init(const BlockPack* blocks); // Easily creates chunk mesh synchronously. CALLER_DELETE ChunkMesh* easyCreateChunkMesh(const Chunk* chunk, MeshTaskType type) { prepareData(chunk); ChunkMesh* mesh = new ChunkMesh; mesh->position = chunk->getVoxelPosition().pos; uploadMeshData(*mesh, createChunkMeshData(type)); return mesh; } // Call one of these before createChunkMesh void prepareData(const Chunk* chunk); // For use with threadpool void prepareDataAsync(ChunkHandle& chunk, ChunkHandle neighbors[NUM_NEIGHBOR_HANDLES]); // TODO(Ben): Unique ptr? // Must call prepareData or prepareDataAsync first CALLER_DELETE ChunkMeshData* createChunkMeshData(MeshTaskType type); // Returns true if the mesh is renderable static bool uploadMeshData(ChunkMesh& mesh, ChunkMeshData* meshData); // Frees buffers AND deletes memory. mesh Pointer is invalid after calling. static void freeChunkMesh(CALLEE_DELETE ChunkMesh* mesh); void freeBuffers(); int bx, by, bz; // Block iterators int blockIndex; ui16 blockID; const Block* block; const PlanetHeightData* heightData; ui8v3 voxelPosOffset; // Heightmap data PlanetHeightData heightDataBuffer[CHUNK_LAYER]; // Voxel data arrays ui16 blockData[PADDED_CHUNK_SIZE]; ui16 tertiaryData[PADDED_CHUNK_SIZE]; const BlockPack* blocks; VoxelPosition3D chunkVoxelPos; private: void addBlock(); void addQuad(int face, int rightAxis, int frontAxis, int leftOffset, int backOffset, int rightStretchIndex, const ui8v2& texOffset, f32 ambientOcclusion[]); void computeAmbientOcclusion(int upOffset, int frontOffset, int rightOffset, f32 ambientOcclusion[]); void addFlora(); void addFloraQuad(const ui8v3* positions, FloraQuadData& data); int tryMergeQuad(VoxelQuad* quad, std::vector& quads, int face, int rightAxis, int frontAxis, int leftOffset, int backOffset, int rightStretchIndex, const ui8v2& texOffset); void addLiquid(); int getLiquidLevel(int blockIndex, const Block& block); bool shouldRenderFace(int offset); int getOcclusion(const Block& block); ui8 getBlendMode(const BlendType& blendType); static void buildTransparentVao(ChunkMesh& cm); static void buildCutoutVao(ChunkMesh& cm); static void buildVao(ChunkMesh& cm); static void buildWaterVao(ChunkMesh& cm); ui16 m_quadIndices[PADDED_CHUNK_SIZE][6]; ui16 m_wvec[CHUNK_SIZE]; std::vector m_finalVerts[6]; std::vector m_floraQuads; std::vector m_quads[6]; ui32 m_numQuads; BlockTextureMethodParams m_textureMethodParams[6][2]; // TODO(Ben): Change this up a bit std::vector _waterVboVerts; ChunkMeshData* m_chunkMeshData; int m_highestY; int m_lowestY; int m_highestX; int m_lowestX; int m_highestZ; int m_lowestZ; const PlanetHeightData* m_chunkHeightData; static PlanetHeightData defaultChunkHeightData[CHUNK_LAYER]; int wSize; // ui32 m_finalQuads[7000]; BlockVertex m_topVerts[4100]; }; ================================================ FILE: SoA/ChunkQuery.cpp ================================================ #include "stdafx.h" #include "ChunkQuery.h" #include "ChunkGrid.h" void ChunkQuery::release() { grid->releaseQuery(this); } ================================================ FILE: SoA/ChunkQuery.h ================================================ // // ChunkQuery.h // Seed of Andromeda // // Created by Benjamin Arnold on 1 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Class for querying for a chunk with a certain generation level // #pragma once #ifndef ChunkQuery_h__ #define ChunkQuery_h__ #include "ChunkHandle.h" #include "GenerateTask.h" #include enum ChunkGenLevel { GEN_NONE = 0, GEN_TERRAIN, GEN_FLORA, GEN_SCRIPT, GEN_DONE }; class ChunkGrid; class ChunkQuery { friend class ChunkGenerator; friend class GenerateTask; friend class ChunkGrid; public: void release(); /// Blocks current thread until the query is finished void block() { std::unique_lock lck(m_lock); if (isFinished()) return; m_cond.wait(lck); } const bool& isFinished() const { return m_isFinished; } i32v3 chunkPos; ChunkGenLevel genLevel; GenerateTask genTask; ///< For if the query results in generation ChunkHandle chunk; ///< Gets set on submitQuery bool shouldRelease; ChunkGrid* grid; private: bool m_isFinished; std::mutex m_lock; std::condition_variable m_cond; }; #endif // ChunkQuery_h__ ================================================ FILE: SoA/ChunkRenderer.cpp ================================================ #include "stdafx.h" #include "ChunkRenderer.h" #include "Camera.h" #include "Chunk.h" #include "ChunkMeshManager.h" #include "Frustum.h" #include "GameManager.h" #include "GameRenderParams.h" #include "GeometrySorter.h" #include "RenderUtils.h" #include "ShaderLoader.h" #include "SoaOptions.h" #include "soaUtils.h" volatile f32 ChunkRenderer::fadeDist = 1.0f; f32m4 ChunkRenderer::worldMatrix = f32m4(1.0f); VGIndexBuffer ChunkRenderer::sharedIBO = 0; void ChunkRenderer::init() { // Not thread safe if (!sharedIBO) { // Create shared IBO if needed std::vector indices; const int NUM_INDICES = 589824; indices.resize(NUM_INDICES); ui32 j = 0u; for (size_t i = 0; i < indices.size() - 12u; i += 6u) { indices[i] = j; indices[i + 1] = j + 1u; indices[i + 2] = j + 2u; indices[i + 3] = j + 2u; indices[i + 4] = j + 3u; indices[i + 5] = j; j += 4u; } glGenBuffers(1, &sharedIBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sharedIBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, NUM_INDICES * sizeof(ui32), NULL, GL_STATIC_DRAW); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, NUM_INDICES * sizeof(ui32), indices.data()); //arbitrarily set to 300000 } { // Opaque m_opaqueProgram = ShaderLoader::createProgramFromFile("Shaders/BlockShading/standardShading.vert", "Shaders/BlockShading/standardShading.frag"); m_opaqueProgram.use(); glUniform1i(m_opaqueProgram.getUniform("unTextures"), 0); } // TODO(Ben): Fix the shaders { // Transparent // m_transparentProgram = ShaderLoader::createProgramFromFile("Shaders/BlockShading/standardShading.vert", // "Shaders/BlockShading/cutoutShading.frag"); } { // Cutout m_cutoutProgram = ShaderLoader::createProgramFromFile("Shaders/BlockShading/standardShading.vert", "Shaders/BlockShading/cutoutShading.frag"); m_cutoutProgram.use(); glUniform1i(m_cutoutProgram.getUniform("unTextures"), 0); } { // Water // m_waterProgram = ShaderLoader::createProgramFromFile("Shaders/WaterShading/WaterShading.vert", // "Shaders/WaterShading/WaterShading.frag"); } vg::GLProgram::unuse(); } void ChunkRenderer::dispose() { if (m_opaqueProgram.isCreated()) m_opaqueProgram.dispose(); if (m_transparentProgram.isCreated()) m_transparentProgram.dispose(); if (m_cutoutProgram.isCreated()) m_cutoutProgram.dispose(); if (m_waterProgram.isCreated()) m_waterProgram.dispose(); } // TODO: blockAmbient variables were going unused, what are they for? void ChunkRenderer::beginOpaque(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor VORB_MAYBE_UNUSED /*= f32v3(1.0f)*/, const f32v3& ambient /*= f32v3(0.0f)*/) { m_opaqueProgram.use(); glUniform3fv(m_opaqueProgram.getUniform("unLightDirWorld"), 1, &(sunDir[0])); glUniform1f(m_opaqueProgram.getUniform("unSpecularExponent"), soaOptions.get(OPT_SPECULAR_EXPONENT).value.f); glUniform1f(m_opaqueProgram.getUniform("unSpecularIntensity"), soaOptions.get(OPT_SPECULAR_INTENSITY).value.f * 0.3f); // Bind the block textures glActiveTexture(GL_TEXTURE0); glUniform1i(m_opaqueProgram.getUniform("unTextures"), 0); // TODO(Ben): Temporary glBindTexture(GL_TEXTURE_2D_ARRAY, textureAtlas); // f32 blockAmbient = 0.000f; glUniform3fv(m_opaqueProgram.getUniform("unAmbientLight"), 1, &ambient[0]); glUniform3fv(m_opaqueProgram.getUniform("unSunColor"), 1, &sunDir[0]); glUniform1f(m_opaqueProgram.getUniform("unFadeDist"), 100000.0f/*ChunkRenderer::fadeDist*/); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sharedIBO); } void ChunkRenderer::drawOpaque(const ChunkMesh *cm, const f64v3 &PlayerPos, const f32m4 &VP) const { if (cm->vaoID == 0) return; setMatrixTranslation(worldMatrix, f64v3(cm->position), PlayerPos); f32m4 MVP = VP * worldMatrix; glUniformMatrix4fv(m_opaqueProgram.getUniform("unWVP"), 1, GL_FALSE, &MVP[0][0]); glUniformMatrix4fv(m_opaqueProgram.getUniform("unW"), 1, GL_FALSE, &worldMatrix[0][0]); glBindVertexArray(cm->vaoID); const ChunkMeshRenderData& chunkMeshInfo = cm->renderData; //top if (chunkMeshInfo.pyVboSize && PlayerPos.y > cm->position.y + chunkMeshInfo.lowestY) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.pyVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pyVboOff * sizeof(GLuint))); } //front if (chunkMeshInfo.pzVboSize && PlayerPos.z > cm->position.z + chunkMeshInfo.lowestZ){ glDrawElements(GL_TRIANGLES, chunkMeshInfo.pzVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pzVboOff * sizeof(GLuint))); } //back if (chunkMeshInfo.nzVboSize && PlayerPos.z < cm->position.z + chunkMeshInfo.highestZ){ glDrawElements(GL_TRIANGLES, chunkMeshInfo.nzVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nzVboOff * sizeof(GLuint))); } //left if (chunkMeshInfo.nxVboSize && PlayerPos.x < cm->position.x + chunkMeshInfo.highestX){ glDrawElements(GL_TRIANGLES, chunkMeshInfo.nxVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nxVboOff * sizeof(GLuint))); } //right if (chunkMeshInfo.pxVboSize && PlayerPos.x > cm->position.x + chunkMeshInfo.lowestX){ glDrawElements(GL_TRIANGLES, chunkMeshInfo.pxVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pxVboOff * sizeof(GLuint))); } //bottom if (chunkMeshInfo.nyVboSize && PlayerPos.y < cm->position.y + chunkMeshInfo.highestY){ glDrawElements(GL_TRIANGLES, chunkMeshInfo.nyVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nyVboOff * sizeof(GLuint))); } glBindVertexArray(0); } void ChunkRenderer::drawOpaqueCustom(const ChunkMesh* cm, vg::GLProgram& m_program, const f64v3& PlayerPos, const f32m4& VP) { if (cm->vaoID == 0) return; setMatrixTranslation(worldMatrix, f64v3(cm->position), PlayerPos); f32m4 MVP = VP * worldMatrix; glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &MVP[0][0]); glUniformMatrix4fv(m_program.getUniform("unW"), 1, GL_FALSE, &worldMatrix[0][0]); glBindVertexArray(cm->vaoID); const ChunkMeshRenderData& chunkMeshInfo = cm->renderData; //top if (chunkMeshInfo.pyVboSize && PlayerPos.y > cm->position.y + chunkMeshInfo.lowestY) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.pyVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pyVboOff * sizeof(GLuint))); } //front if (chunkMeshInfo.pzVboSize && PlayerPos.z > cm->position.z + chunkMeshInfo.lowestZ) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.pzVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pzVboOff * sizeof(GLuint))); } //back if (chunkMeshInfo.nzVboSize && PlayerPos.z < cm->position.z + chunkMeshInfo.highestZ) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.nzVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nzVboOff * sizeof(GLuint))); } //left if (chunkMeshInfo.nxVboSize && PlayerPos.x < cm->position.x + chunkMeshInfo.highestX) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.nxVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nxVboOff * sizeof(GLuint))); } //right if (chunkMeshInfo.pxVboSize && PlayerPos.x > cm->position.x + chunkMeshInfo.lowestX) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.pxVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.pxVboOff * sizeof(GLuint))); } //bottom if (chunkMeshInfo.nyVboSize && PlayerPos.y < cm->position.y + chunkMeshInfo.highestY) { glDrawElements(GL_TRIANGLES, chunkMeshInfo.nyVboSize, GL_UNSIGNED_INT, (void*)(chunkMeshInfo.nyVboOff * sizeof(GLuint))); } glBindVertexArray(0); } void ChunkRenderer::beginTransparent(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor VORB_MAYBE_UNUSED /*= f32v3(1.0f)*/, const f32v3& ambient /*= f32v3(0.0f)*/) { m_transparentProgram.use(); glUniform3fv(m_transparentProgram.getUniform("unLightDirWorld"), 1, &(sunDir[0])); glUniform1f(m_transparentProgram.getUniform("unSpecularExponent"), soaOptions.get(OPT_SPECULAR_EXPONENT).value.f); glUniform1f(m_transparentProgram.getUniform("unSpecularIntensity"), soaOptions.get(OPT_SPECULAR_INTENSITY).value.f * 0.3f); // Bind the block textures glActiveTexture(GL_TEXTURE0); glUniform1i(m_transparentProgram.getUniform("unTextures"), 0); // TODO(Ben): Temporary glBindTexture(GL_TEXTURE_2D_ARRAY, textureAtlas); // f32 blockAmbient = 0.000f; glUniform3fv(m_transparentProgram.getUniform("unAmbientLight"), 1, &ambient[0]); glUniform3fv(m_transparentProgram.getUniform("unSunColor"), 1, &sunDir[0]); glUniform1f(m_transparentProgram.getUniform("unFadeDist"), 100000.0f/*ChunkRenderer::fadeDist*/); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sharedIBO); } void ChunkRenderer::drawTransparent(const ChunkMesh *cm, const f64v3 &playerPos, const f32m4 &VP) const { if (cm->transVaoID == 0) return; setMatrixTranslation(worldMatrix, f64v3(cm->position), playerPos); f32m4 MVP = VP * worldMatrix; glUniformMatrix4fv(m_transparentProgram.getUniform("MVP"), 1, GL_FALSE, &MVP[0][0]); glUniformMatrix4fv(m_transparentProgram.getUniform("M"), 1, GL_FALSE, &worldMatrix[0][0]); glBindVertexArray(cm->transVaoID); glDrawElements(GL_TRIANGLES, cm->renderData.transVboSize, GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } void ChunkRenderer::beginCutout(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor VORB_MAYBE_UNUSED /*= f32v3(1.0f)*/, const f32v3& ambient /*= f32v3(0.0f)*/) { m_cutoutProgram.use(); glUniform3fv(m_cutoutProgram.getUniform("unLightDirWorld"), 1, &(sunDir[0])); glUniform1f(m_cutoutProgram.getUniform("unSpecularExponent"), soaOptions.get(OPT_SPECULAR_EXPONENT).value.f); glUniform1f(m_cutoutProgram.getUniform("unSpecularIntensity"), soaOptions.get(OPT_SPECULAR_INTENSITY).value.f * 0.3f); // Bind the block textures glActiveTexture(GL_TEXTURE0); glUniform1i(m_cutoutProgram.getUniform("unTextures"), 0); // TODO(Ben): Temporary glBindTexture(GL_TEXTURE_2D_ARRAY, textureAtlas); // f32 blockAmbient = 0.000f; glUniform3fv(m_cutoutProgram.getUniform("unAmbientLight"), 1, &ambient[0]); glUniform3fv(m_cutoutProgram.getUniform("unSunColor"), 1, &sunDir[0]); glUniform1f(m_cutoutProgram.getUniform("unFadeDist"), 100000.0f/*ChunkRenderer::fadeDist*/); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sharedIBO); } void ChunkRenderer::drawCutout(const ChunkMesh *cm, const f64v3 &playerPos, const f32m4 &VP) const { if (cm->cutoutVaoID == 0) return; setMatrixTranslation(worldMatrix, f64v3(cm->position), playerPos); f32m4 MVP = VP * worldMatrix; glUniformMatrix4fv(m_cutoutProgram.getUniform("unWVP"), 1, GL_FALSE, &MVP[0][0]); glUniformMatrix4fv(m_cutoutProgram.getUniform("unW"), 1, GL_FALSE, &worldMatrix[0][0]); glBindVertexArray(cm->cutoutVaoID); glDrawElements(GL_TRIANGLES, cm->renderData.cutoutVboSize, GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } void ChunkRenderer::beginLiquid(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor VORB_MAYBE_UNUSED /*= f32v3(1.0f)*/, const f32v3& ambient /*= f32v3(0.0f)*/) { m_waterProgram.use(); glUniform3fv(m_waterProgram.getUniform("unLightDirWorld"), 1, &(sunDir[0])); glUniform1f(m_waterProgram.getUniform("unSpecularExponent"), soaOptions.get(OPT_SPECULAR_EXPONENT).value.f); glUniform1f(m_waterProgram.getUniform("unSpecularIntensity"), soaOptions.get(OPT_SPECULAR_INTENSITY).value.f * 0.3f); // Bind the block textures glActiveTexture(GL_TEXTURE0); glUniform1i(m_waterProgram.getUniform("unTextures"), 0); // TODO(Ben): Temporary glBindTexture(GL_TEXTURE_2D_ARRAY, textureAtlas); // f32 blockAmbient = 0.000f; glUniform3fv(m_waterProgram.getUniform("unAmbientLight"), 1, &ambient[0]); glUniform3fv(m_waterProgram.getUniform("unSunColor"), 1, &sunDir[0]); glUniform1f(m_waterProgram.getUniform("unFadeDist"), 100000.0f/*ChunkRenderer::fadeDist*/); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sharedIBO); } void ChunkRenderer::drawLiquid(const ChunkMesh *cm, const f64v3 &PlayerPos, const f32m4 &VP) const { //use drawWater bool to avoid checking frustum twice if (cm->inFrustum && cm->waterVboID){ setMatrixTranslation(worldMatrix, f64v3(cm->position), PlayerPos); f32m4 MVP = VP * worldMatrix; glUniformMatrix4fv(m_waterProgram.getUniform("MVP"), 1, GL_FALSE, &MVP[0][0]); glUniformMatrix4fv(m_waterProgram.getUniform("M"), 1, GL_FALSE, &worldMatrix[0][0]); glBindVertexArray(cm->waterVaoID); glDrawElements(GL_TRIANGLES, cm->renderData.waterIndexSize, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } } void ChunkRenderer::end() { vg::GLProgram::unuse(); } ================================================ FILE: SoA/ChunkRenderer.h ================================================ #pragma once #ifndef ChunkRenderer_h__ #define ChunkRenderer_h__ #include #include "ChunkMesh.h" class GameRenderParams; class PhysicsBlockMesh; #define CHUNK_DIAGONAL_LENGTH 28.0f class ChunkRenderer { public: // Loads the shaders. Call on render thread. void init(); void dispose(); void beginOpaque(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor = f32v3(1.0f), const f32v3& ambient = f32v3(0.0f)); void drawOpaque(const ChunkMesh* cm, const f64v3& PlayerPos, const f32m4& VP) const; static void drawOpaqueCustom(const ChunkMesh* cm, vg::GLProgram& m_program, const f64v3& PlayerPos, const f32m4& VP); void beginTransparent(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor = f32v3(1.0f), const f32v3& ambient = f32v3(0.0f)); void drawTransparent(const ChunkMesh* cm, const f64v3& playerPos, const f32m4& VP) const; void beginCutout(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor = f32v3(1.0f), const f32v3& ambient = f32v3(0.0f)); void drawCutout(const ChunkMesh* cm, const f64v3& playerPos, const f32m4& VP) const; void beginLiquid(VGTexture textureAtlas, const f32v3& sunDir, const f32v3& lightColor = f32v3(1.0f), const f32v3& ambient = f32v3(0.0f)); void drawLiquid(const ChunkMesh* cm, const f64v3& PlayerPos, const f32m4& VP) const; // Unbinds the shader static void end(); static volatile f32 fadeDist; static VGIndexBuffer sharedIBO; private: static f32m4 worldMatrix; ///< Reusable world matrix for chunks vg::GLProgram m_opaqueProgram; vg::GLProgram m_transparentProgram; vg::GLProgram m_cutoutProgram; vg::GLProgram m_waterProgram; }; #endif // ChunkRenderer_h__ ================================================ FILE: SoA/ChunkSphereComponentUpdater.cpp ================================================ #include "stdafx.h" #include "ChunkSphereComponentUpdater.h" #include "ChunkAccessor.h" #include "ChunkID.h" #include "GameSystem.h" #include "SpaceSystem.h" #include "VoxelSpaceConversions.h" #include "soaUtils.h" #define X_AXIS 0 #define Y_AXIS 1 #define Z_AXIS 2 void ChunkSphereComponentUpdater::update(GameSystem* gameSystem, SpaceSystem* spaceSystem) { for (auto& it : gameSystem->chunkSphere) { ChunkSphereComponent& cmp = it.second; auto& voxelPos = gameSystem->voxelPosition.get(cmp.voxelPosition); ChunkPosition3D chunkPos = VoxelSpaceConversions::voxelToChunk(voxelPos.gridPosition); // Check for grid shift or init if (cmp.currentCubeFace != chunkPos.face) { releaseHandles(cmp); cmp.centerPosition = chunkPos; cmp.currentCubeFace = chunkPos.face; auto& sphericalVoxel = spaceSystem->sphericalVoxel.get(voxelPos.parentVoxel); cmp.chunkGrid = &sphericalVoxel.chunkGrids[chunkPos.face]; initSphere(cmp); } // Check for shift if (chunkPos.pos != cmp.centerPosition) { i32v3 offset = chunkPos.pos - cmp.centerPosition; i32 dx2 = offset.x * offset.x; i32 dy2 = offset.y * offset.y; i32 dz2 = offset.z * offset.z; if (dx2 + dy2 + dz2 == 1) { // Hideous but fast version. Shift by 1. if (dx2) { shiftDirection(cmp, X_AXIS, Y_AXIS, Z_AXIS, offset.x); } else if (dz2) { shiftDirection(cmp, Z_AXIS, Y_AXIS, X_AXIS, offset.z); } else { shiftDirection(cmp, Y_AXIS, X_AXIS, Z_AXIS, offset.y); } cmp.centerPosition = chunkPos.pos; } else { // Slow version. Multi-chunk shift. cmp.offset += chunkPos.pos - cmp.centerPosition; // Scale back to the range for (int i = 0; i < 3; i++) { while (cmp.offset[i] > cmp.radius) cmp.offset[i] -= cmp.width; while (cmp.offset[i] < -cmp.radius) cmp.offset[i] += cmp.width; } cmp.centerPosition = chunkPos.pos; int radius2 = cmp.radius * cmp.radius; // Get all in range slots for (int y = -cmp.radius; y <= cmp.radius; y++) { for (int z = -cmp.radius; z <= cmp.radius; z++) { for (int x = -cmp.radius; x <= cmp.radius; x++) { i32v3 p = cmp.offset + i32v3(x, y, z); // Wrap (I hope this gets optimized) for (int i = 0; i < 3; i++) { if (p[i] < -cmp.radius) { p[i] += cmp.width; } else if (p[i] > cmp.radius) { p[i] -= cmp.width; } } int index = (p.y + cmp.radius) * cmp.layer + (p.z + cmp.radius) * cmp.width + (p.x + cmp.radius); if (cmp.handleGrid[index].isAquired()) { i32v3 diff = cmp.handleGrid[index]->getChunkPosition().pos - cmp.centerPosition; int d2 = selfDot(diff); if (d2 <= radius2) { continue; // Still in range } else { releaseAndDisconnect(cmp, cmp.handleGrid[index]); } } // Check if its in range if (x * x + y * y + z * z <= radius2) { i32v3 chunkPos(cmp.centerPosition.x + x, cmp.centerPosition.y + y, cmp.centerPosition.z + z); // TODO(Ben): Sort cmp.handleGrid[index] = submitAndConnect(cmp, chunkPos); } } } } } } } } void ChunkSphereComponentUpdater::setRadius(ChunkSphereComponent& cmp, ui32 radius) { // Release old handles releaseHandles(cmp); // Set vars cmp.radius = radius; cmp.width = cmp.radius * 2 + 1; cmp.layer = cmp.width * cmp.width; cmp.size = cmp.layer * cmp.width; // Allocate handles initSphere(cmp); } #define GET_INDEX(x, y, z) (((x) + cmp.radius) + ((y) + cmp.radius) * cmp.layer + ((z) + cmp.radius) * cmp.width) void ChunkSphereComponentUpdater::shiftDirection(ChunkSphereComponent& cmp, int axis1, int axis2, int axis3, int offset) { if (offset > 0) { // Release for (auto& o : cmp.acquireOffsets) { i32v3 p; p[axis1] = cmp.offset[axis1] - o.x + 1; p[axis2] = cmp.offset[axis2] + o.y; p[axis3] = cmp.offset[axis3] + o.z; // Wrap (I hope this gets optimized) for (int i = 0; i < 3; i++) { if (p[i] < -cmp.radius) { p[i] += cmp.width; } else if (p[i] > cmp.radius) { p[i] -= cmp.width; } } releaseAndDisconnect(cmp, cmp.handleGrid[GET_INDEX(p.x, p.y, p.z)]); } // Acquire for (auto& o : cmp.acquireOffsets) { i32v3 p; p[axis1] = cmp.offset[axis1] + o.x; p[axis2] = cmp.offset[axis2] + o.y; p[axis3] = cmp.offset[axis3] + o.z; // Wrap (I hope this gets optimized) for (int i = 0; i < 3; i++) { if (p[i] < -cmp.radius) { p[i] += cmp.width; } else if (p[i] > cmp.radius) { p[i] -= cmp.width; } } i32v3 off; off[axis1] = o.x; off[axis2] = o.y; off[axis3] = o.z; cmp.handleGrid[GET_INDEX(p.x, p.y, p.z)] = submitAndConnect(cmp, cmp.centerPosition + off); } cmp.offset[axis1]++; if (cmp.offset[axis1] > cmp.radius) cmp.offset[axis1] = -cmp.radius; } else { for (auto& o : cmp.acquireOffsets) { i32v3 p; p[axis1] = cmp.offset[axis1] + o.x - 1; p[axis2] = cmp.offset[axis2] + o.y; p[axis3] = cmp.offset[axis3] + o.z; // Wrap (I hope this gets optimized) for (int i = 0; i < 3; i++) { if (p[i] < -cmp.radius) { p[i] += cmp.width; } else if (p[i] > cmp.radius) { p[i] -= cmp.width; } } releaseAndDisconnect(cmp, cmp.handleGrid[GET_INDEX(p.x, p.y, p.z)]); } // Acquire for (auto& o : cmp.acquireOffsets) { i32v3 p; p[axis1] = cmp.offset[axis1] - o.x; p[axis2] = cmp.offset[axis2] + o.y; p[axis3] = cmp.offset[axis3] + o.z; // Wrap (I hope this gets optimized) for (int i = 0; i < 3; i++) { if (p[i] < -cmp.radius) { p[i] += cmp.width; } else if (p[i] > cmp.radius) { p[i] -= cmp.width; } } i32v3 off; off[axis1] = -o.x; off[axis2] = o.y; off[axis3] = o.z; cmp.handleGrid[GET_INDEX(p.x, p.y, p.z)] = submitAndConnect(cmp, cmp.centerPosition + off); } cmp.offset[axis1]--; if (cmp.offset[axis1] < -cmp.radius) cmp.offset[axis1] = cmp.radius; } } #undef GET_INDEX ChunkHandle ChunkSphereComponentUpdater::submitAndConnect(ChunkSphereComponent& cmp, const i32v3& chunkPos) { ChunkHandle h = cmp.chunkGrid->submitQuery(chunkPos, GEN_DONE, true)->chunk.acquire(); // TODO(Ben): meshableNeighbors // Acquire the 26 neighbors // TODO(Ben): Could optimize ChunkAccessor& accessor = cmp.chunkGrid->accessor; { // Left ChunkID id = h.getID(); id.x--; h->neighbor.left = accessor.acquire(id); } { // Right ChunkID id = h.getID(); id.x++; h->neighbor.right = accessor.acquire(id); } { // Bottom ChunkID id = h.getID(); id.y--; h->neighbor.bottom = accessor.acquire(id); } { // Top ChunkID id = h.getID(); id.y++; h->neighbor.top = accessor.acquire(id); } { // Back ChunkID id = h.getID(); id.z--; h->neighbor.back = accessor.acquire(id); } { // Front ChunkID id = h.getID(); id.z++; h->neighbor.front = accessor.acquire(id); } cmp.chunkGrid->onNeighborsAcquire(h); return h; #undef GET_HALF } void ChunkSphereComponentUpdater::releaseAndDisconnect(ChunkSphereComponent& cmp, ChunkHandle& h) { // Call the event first to prevent race condition cmp.chunkGrid->onNeighborsRelease(h); h->neighbor.left.release(); h->neighbor.right.release(); h->neighbor.back.release(); h->neighbor.front.release(); h->neighbor.bottom.release(); h->neighbor.top.release(); h.release(); } void ChunkSphereComponentUpdater::releaseHandles(ChunkSphereComponent& cmp) { if (cmp.handleGrid) { for (int i = 0; i < cmp.size; i++) { if (cmp.handleGrid[i].isAquired()) releaseAndDisconnect(cmp, cmp.handleGrid[i]); } delete[] cmp.handleGrid; cmp.handleGrid = nullptr; } } void ChunkSphereComponentUpdater::initSphere(ChunkSphereComponent& cmp) { cmp.handleGrid = new ChunkHandle[cmp.size]; cmp.offset = i32v3(0); // Pre-compute offsets int radius2 = cmp.radius * cmp.radius; // Get all in range slots for (int y = -cmp.radius; y <= cmp.radius; y++) { for (int z = -cmp.radius; z <= cmp.radius; z++) { for (int x = -cmp.radius; x <= cmp.radius; x++) { // Check if its in range if (x * x + y * y + z * z <= radius2) { int index = (y + cmp.radius) * cmp.layer + (z + cmp.radius) * cmp.width + (x + cmp.radius); i32v3 chunkPos(cmp.centerPosition.x + x, cmp.centerPosition.y + y, cmp.centerPosition.z + z); // TODO(Ben): Sort cmp.handleGrid[index] = submitAndConnect(cmp, chunkPos); } } } } // Determine +x acquire offsets int index = 0; for (int y = -cmp.radius; y <= cmp.radius; y++) { for (int z = -cmp.radius; z <= cmp.radius; z++) { for (int x = -cmp.radius; x <= cmp.radius; x++, index++) { if (cmp.handleGrid[index].isAquired()) { if (x == cmp.radius || !cmp.handleGrid[index + 1].isAquired()) { cmp.acquireOffsets.emplace_back(x + 1, y, z); } } } } } } ================================================ FILE: SoA/ChunkSphereComponentUpdater.h ================================================ // // ChunkSphereAcquirer.h // Seed of Andromeda // // Created by Benjamin Arnold on 8 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Acquires a sphere of chunks around an agent. // #pragma once #ifndef ChunkSphereAcquirer_h__ #define ChunkSphereAcquirer_h__ #include "ChunkHandle.h" #include "GameSystemComponents.h" class GameSystem; class SpaceSystem; class ChunkAccessor; class ChunkSphereComponentUpdater { public: void update(GameSystem* gameSystem, SpaceSystem* spaceSystem); void setRadius(ChunkSphereComponent& cmp, ui32 radius); private: void shiftDirection(ChunkSphereComponent& cmp, int axis1, int axis2, int axis3, int offset); // Submits a gen query and connects to neighbors ChunkHandle submitAndConnect(ChunkSphereComponent& cmp, const i32v3& chunkPos); void releaseAndDisconnect(ChunkSphereComponent& cmp, ChunkHandle& h); void releaseHandles(ChunkSphereComponent& cmp); void initSphere(ChunkSphereComponent& cmp); }; #endif // ChunkSphereAcquirer_h__ ================================================ FILE: SoA/ChunkUpdater.cpp ================================================ #include "stdafx.h" #include "ChunkUpdater.h" #include "BlockPack.h" #include "CAEngine.h" #include "Errors.h" #include "GameManager.h" #include "VoxelLightEngine.h" #include "VoxelNavigation.inl" #include "VoxelUtils.h" #include "VoxelUpdateOrder.inl" BlockPack* ChunkUpdater::blockPack = nullptr; void ChunkUpdater::randomBlockUpdates(PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED) { //if (!chunk->isAccessible) return; //int blockIndex, blockIndex2, blockID; //ChunkStates newState; //bool needsSetup; //Chunk *owner; //Chunk* left = chunk->left; //Chunk* right = chunk->right; //Chunk* front = chunk->front; //Chunk* back = chunk->back; //Chunk* bottom = chunk->bottom; //Chunk* top = chunk->top; //i32v3 pos; //Chunk* lockedChunk = nullptr; //for (int i = 0; i < 15; i++) { // needsSetup = false; // blockIndex = RandomUpdateOrder[chunk->blockUpdateIndex++]; // if (chunk->blockUpdateIndex == CHUNK_SIZE) chunk->blockUpdateIndex = 0; // pos = getPosFromBlockIndex(blockIndex); // // blockID = chunk->getBlockIDSafe(lockedChunk, blockIndex); // if (Blocks[blockID].emitterRandom){ // // uncomment for falling leaves // // particleEngine.AddAnimatedParticles(1, f64v3(position.x + x, Y + y, position.z + z), Blocks[b].emitterRandom); // } // //TODO: Replace most of this with block update scripts // //TODO(Ben): There are race conditions here! // if (blockID >= LOWWATER && blockID < LOWWATER + 5 && (GETBLOCKID(vvox::getBottomBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner)) < LOWWATER)) { // chunk->setBlockDataSafe(lockedChunk, blockIndex, NONE); // owner->numBlocks--; // needsSetup = true; // newState = ChunkStates::WATERMESH; // ChunkUpdater::addBlockToUpdateList(chunk, lockedChunk, blockIndex); // } else if (blockID == FIRE) { // // TODO(Ben): Update // //updateFireBlock(chunkManager, physicsEngine, chunk, blockIndex); // needsSetup = true; // newState = ChunkStates::MESH; // } else if (blockID == DIRTGRASS){ // ui32 bt = GETBLOCKID(vvox::getTopBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner)); // // Tmp debugging // if (bt > Blocks.size()) { // pError("Invalid block in update!: " + std::to_string(bt)); // } // if ((Blocks[bt].collide && bt != LEAVES1) || bt >= LOWWATER){ // chunk->setBlockDataSafe(lockedChunk, blockIndex, DIRT); // needsSetup = true; // newState = ChunkStates::MESH; // } // } else if (blockID == DIRT){ // if ((rand() % 10 == 0) && (GETBLOCKID(vvox::getTopBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner)) == NONE) && // (GETBLOCKID(vvox::getLeftBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner)) == DIRTGRASS || // GETBLOCKID(vvox::getRightBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner)) == DIRTGRASS || // GETBLOCKID(vvox::getFrontBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner)) == DIRTGRASS || // GETBLOCKID(vvox::getBackBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner)) == DIRTGRASS)) { // chunk->setBlockDataSafe(lockedChunk, blockIndex, DIRTGRASS); // needsSetup = true; // newState = ChunkStates::MESH; // } // } // if (needsSetup){ // // chunk->changeState(newState); // if (pos.x == 0){ // if (left && left->isAccessible){ // left->changeState(newState); // } // } else if (pos.x == CHUNK_WIDTH - 1){ // if (right && right->isAccessible){ // right->changeState(newState); // } // } // if (pos.y == 0){ // if (bottom && bottom->isAccessible){ // bottom->changeState(newState); // } // } else if (pos.y == CHUNK_WIDTH - 1){ // if (top && top->isAccessible){ // top->changeState(newState); // } // } // if (pos.z == 0){ // if (back && back->isAccessible){ // back->changeState(newState); // } // } else if (pos.z == CHUNK_WIDTH - 1){ // if (front && front->isAccessible){ // front->changeState(newState); // } // } // } //} //if (lockedChunk) lockedChunk->unlock(); } void ChunkUpdater::placeBlockSafe(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, BlockIndex blockIndex VORB_UNUSED, BlockID blockData VORB_UNUSED) { /* vvox::swapLockedChunk(chunk, lockedChunk); placeBlock(chunk, lockedChunk, blockIndex, blockData);*/ } void ChunkUpdater::placeBlockNoUpdate(Chunk* chunk, BlockIndex blockIndex, BlockID blockType) { chunk->blocks.set(blockIndex, blockType); chunk->flagDirty(); //Block &block = GETBLOCK(blockType); //if (chunk->getBlockData(blockIndex) == NONE) { // chunk->numBlocks++; //} //chunk->setBlockData(blockIndex, blockType); //if (block.spawnerVal || block.sinkVal) { // chunk->spawnerBlocks.push_back(blockIndex); //} //const i32v3 pos = getPosFromBlockIndex(blockIndex); //if (block.emitter) { // particleEngine.addEmitter(block.emitter, f64v3(chunk->voxelPosition.x + pos.x, chunk->voxelPosition.y + pos.y, chunk->voxelPosition.z + pos.z), blockType); //} //// If its a plant, we need to do some extra iteration //if (block.floraHeight) { // placeFlora(chunk, blockIndex, blockType); //} ////Check for light removal due to block occlusion //if (block.blockLight) { // if (chunk->getSunlight(blockIndex)) { // if (chunk->getSunlight(blockIndex) == MAXLIGHT) { // chunk->setSunlight(blockIndex, 0); // chunk->sunRemovalList.push_back(blockIndex); // } else { // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } // if (chunk->getLampLight(blockIndex)) { // chunk->lampLightRemovalQueue.emplace(blockIndex, chunk->getLampLight(blockIndex)); // chunk->setLampLight(blockIndex, 0); // } //} else if (block.colorFilter != f32v3(1.0f)) { // //This will pull light from neighbors // chunk->lampLightRemovalQueue.emplace(blockIndex, chunk->getLampLight(blockIndex)); // chunk->setLampLight(blockIndex, 0); //} ////Light placement //if (block.lightColorPacked) { // chunk->setLampLight(blockIndex, block.lightColorPacked); // chunk->lampLightUpdateQueue.emplace(blockIndex, block.lightColorPacked); //} //if (GETBLOCKID(blockType) >= LOWWATER) { // chunk->changeState(ChunkStates::WATERMESH); // updateNeighborStates(chunk, pos, ChunkStates::WATERMESH); //} else { // chunk->changeState(ChunkStates::MESH); // updateNeighborStates(chunk, pos, ChunkStates::MESH); //} //chunk->dirty = true; } //Simplified placeBlock for liquid physics void ChunkUpdater::placeBlockFromLiquidPhysics(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED, int blockType VORB_UNUSED) { //Block &block = GETBLOCK(blockType); //if (chunk->getBlockData(blockIndex) == NONE) { // chunk->numBlocks++; //} //chunk->setBlockData(blockIndex, blockType); ////Check for light removal due to block occlusion //if (block.blockLight) { // if (chunk->getSunlight(blockIndex)){ // if (chunk->getSunlight(blockIndex) == MAXLIGHT){ // chunk->setSunlight(blockIndex, 0); // chunk->sunRemovalList.push_back(blockIndex); // } else { // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } // if (chunk->getLampLight(blockIndex)){ // chunk->lampLightRemovalQueue.emplace(blockIndex, chunk->getLampLight(blockIndex)); // chunk->setLampLight(blockIndex, 0); // } //} ////Light placement //if (block.lightColorPacked) { // chunk->setLampLight(blockIndex, block.lightColorPacked); // chunk->lampLightUpdateQueue.emplace(blockIndex, block.lightColorPacked); //} //ChunkUpdater::addBlockToUpdateList(chunk, lockedChunk, blockIndex); //chunk->dirty = true; } void ChunkUpdater::placeBlockFromLiquidPhysicsSafe(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED, int blockType VORB_UNUSED) { /* vvox::swapLockedChunk(chunk, lockedChunk); placeBlockFromLiquidPhysics(chunk, lockedChunk, blockIndex, blockType);*/ } void ChunkUpdater::removeBlock(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED, bool isBreak VORB_UNUSED, double force VORB_UNUSED, f32v3 explodeDir VORB_UNUSED) { //int blockID = chunk->getBlockID(blockIndex); //const Block &block = Blocks[blockID]; //float explodeDist = vmath::length(explodeDir); //const i32v3 pos = getPosFromBlockIndex(blockIndex); //if (chunk->getBlockID(blockIndex) == 0) return; //GLbyte da, db, dc; ////Falling check //if (explodeDist){ // GLubyte d; // da = (GLbyte)(explodeDir.x); // db = (GLbyte)(explodeDir.y); // dc = (GLbyte)(explodeDir.z); // if (explodeDist > 255){ // d = 255; // } else{ // d = (GLubyte)(explodeDist); // } // physicsEngine->addFallingCheckNode(FallingCheckNode(chunk, blockIndex, da, db, dc, d)); //} else{ // physicsEngine->addFallingCheckNode(FallingCheckNode(chunk, blockIndex)); //} ////If we are braking the block rather than removing it, it should explode or emit particles //if (isBreak) { // if (block.explosivePower){ // f64v3 dtmp(chunk->voxelPosition.x + pos.x, chunk->voxelPosition.y + pos.y, chunk->voxelPosition.z + pos.z); // physicsEngine->addExplosion(ExplosionNode(dtmp, blockID)); // } // if (block.emitterOnBreak && block.explosivePower == 0){ // // particleEngine.addEmitter(block.emitterOnBreak, f64v3(chunk->voxelPosition.x + blockIndex%CHUNK_WIDTH, chunk->voxelPosition.y + blockIndex / CHUNK_LAYER, chunk->voxelPosition.z + (blockIndex%CHUNK_LAYER) / CHUNK_WIDTH), blockID); // } // if (explodeDist){ // f32 expForce = vmath::length(explodeDir); // expForce = pow(0.89f, expForce)*0.6f; // if (expForce < 0.0f) expForce = 0.0f; // breakBlock(chunk, pos.x, pos.y, pos.z, blockID, force, -vmath::normalize(explodeDir)*expForce); // } else{ // breakBlock(chunk, pos.x, pos.y, pos.z, blockID, force); // } //} //chunk->setBlockData(blockIndex, NONE); ////Update lighting //if (block.blockLight || block.lightColorPacked) { // //This will pull light from neighbors // chunk->lampLightRemovalQueue.emplace(blockIndex, chunk->getLampLight(blockIndex)); // chunk->setLampLight(blockIndex, 0); // //sunlight update // if (pos.y < CHUNK_WIDTH - 1) { // if (chunk->getSunlight(blockIndex + CHUNK_LAYER) == MAXLIGHT) { // chunk->setSunlight(blockIndex, MAXLIGHT); // chunk->sunExtendList.push_back(blockIndex); // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } else if (chunk->top && chunk->top->isAccessible) { // if (chunk->top->getSunlight(blockIndex + CHUNK_LAYER - CHUNK_SIZE) == MAXLIGHT) { // chunk->setSunlight(blockIndex, MAXLIGHT); // chunk->sunExtendList.push_back(blockIndex); // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } //} //// If its a plant, we need to do some extra iteration //if (block.floraHeight) { // removeFlora(chunkManager, physicsEngine, chunk, lockedChunk, blockIndex, blockID); //} //ChunkUpdater::addBlockToUpdateList(chunk, lockedChunk, blockIndex); //chunk->numBlocks--; //if (chunk->numBlocks < 0) chunk->numBlocks = 0; //chunk->changeState(ChunkStates::MESH); //chunk->dirty = 1; //updateNeighborStates(chunk, pos, ChunkStates::MESH); } void ChunkUpdater::removeBlockSafe(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED, bool isBreak VORB_UNUSED, double force VORB_UNUSED, f32v3 explodeDir VORB_UNUSED) { /* vvox::swapLockedChunk(chunk, lockedChunk); removeBlock(chunkManager, physicsEngine, chunk, lockedChunk, blockIndex, isBreak, force, explodeDir);*/ } void ChunkUpdater::removeBlockFromLiquidPhysics(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED) { //const Block &block = chunk->getBlock(blockIndex); //const i32v3 pos = getPosFromBlockIndex(blockIndex); //chunk->setBlockData(blockIndex, NONE); ////Update lighting //if (block.blockLight || block.lightColorPacked) { // //This will pull light from neighbors // chunk->lampLightRemovalQueue.emplace(blockIndex, chunk->getLampLight(blockIndex)); // chunk->setLampLight(blockIndex, 0); // //sunlight update // if (pos.y < CHUNK_WIDTH - 1) { // if (chunk->getSunlight(blockIndex + CHUNK_LAYER) == MAXLIGHT) { // chunk->setSunlight(blockIndex, MAXLIGHT); // chunk->sunExtendList.push_back(blockIndex); // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } else if (chunk->top && chunk->top->isAccessible) { // if (chunk->top->getSunlight(blockIndex + CHUNK_LAYER - CHUNK_SIZE) == MAXLIGHT) { // chunk->setSunlight(blockIndex, MAXLIGHT); // chunk->sunExtendList.push_back(blockIndex); // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } // } else { // //This will pull light from neighbors // chunk->sunlightRemovalQueue.emplace(blockIndex, chunk->getSunlight(blockIndex)); // chunk->setSunlight(blockIndex, 0); // } //} //ChunkUpdater::addBlockToUpdateList(chunk, lockedChunk, blockIndex); //chunk->numBlocks--; //if (chunk->numBlocks < 0) chunk->numBlocks = 0; //chunk->changeState(ChunkStates::WATERMESH); //chunk->dirty = 1; } void ChunkUpdater::removeBlockFromLiquidPhysicsSafe(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED) { /* vvox::swapLockedChunk(chunk, lockedChunk); removeBlockFromLiquidPhysics(chunk, lockedChunk, blockIndex);*/ } void ChunkUpdater::updateNeighborStates(Chunk* chunk VORB_UNUSED, const i32v3& pos VORB_UNUSED, ChunkStates state VORB_UNUSED) { /* if (pos.x == 0){ if (chunk->left){ chunk->left->changeState(state); } } else if (pos.x == CHUNK_WIDTH - 1){ if (chunk->right){ chunk->right->changeState(state); } } if (pos.y == 0){ if (chunk->bottom){ chunk->bottom->changeState(state); } } else if (pos.y == CHUNK_WIDTH - 1){ if (chunk->top){ chunk->top->changeState(state); } } if (pos.z == 0){ if (chunk->back){ chunk->back->changeState(state); } } else if (pos.z == CHUNK_WIDTH - 1){ if (chunk->front){ chunk->front->changeState(state); } }*/ } void ChunkUpdater::updateNeighborStates(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ChunkStates state VORB_UNUSED) { /* const i32v3 pos = getPosFromBlockIndex(blockIndex); if (pos.x == 0){ if (chunk->left){ chunk->left->changeState(state); } } else if (pos.x == CHUNK_WIDTH - 1){ if (chunk->right){ chunk->right->changeState(state); } } if (pos.y == 0){ if (chunk->bottom){ chunk->bottom->changeState(state); } } else if (pos.y == CHUNK_WIDTH - 1){ if (chunk->top){ chunk->top->changeState(state); } } if (pos.z == 0){ if (chunk->back){ chunk->back->changeState(state); } } else if (pos.z == CHUNK_WIDTH - 1){ if (chunk->front){ chunk->front->changeState(state); } }*/ } void ChunkUpdater::updateBlockAndNeighbors(VoxelUpdateBufferer& bufferer VORB_UNUSED, Chunk* chunk VORB_UNUSED, BlockIndex index VORB_UNUSED) { throw 33; } void ChunkUpdater::updateBlockAndNeighbors(VoxelUpdateBufferer& bufferer VORB_UNUSED, Chunk* chunk VORB_UNUSED, BlockIndex index VORB_UNUSED, BlockID id VORB_UNUSED) { // int phys; // TODO(Matthew): This statement wasn't used, revisit this function to make sure it is behaving correctly. //const i32v3 pos = getPosFromBlockIndex(id); // ChunkHandle& left = chunk->neighbor.left; // ChunkHandle& right = chunk->neighbor.right; // ChunkHandle& front = chunk->neighbor.front; // ChunkHandle& back = chunk->neighbor.back; // ChunkHandle& top = chunk->neighbor.top; // ChunkHandle& bottom = chunk->neighbor.bottom; //if ((phys = chunk->getBlockSafe(lockedChunk, c).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c); //} //if (pos.x > 0){ //left // if ((phys = chunk->getBlockSafe(lockedChunk, c - 1).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c - 1); // } //} else if (left && left->isAccessible){ // if ((phys = left->getBlockSafe(lockedChunk, c + CHUNK_WIDTH - 1).caIndex) > -1) { // left->addPhysicsUpdate(phys, c + CHUNK_WIDTH - 1); // } //} else{ // return; //} //if (pos.x < CHUNK_WIDTH - 1){ //right // if ((phys = chunk->getBlockSafe(lockedChunk, c + 1).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c + 1); // } //} else if (right && right->isAccessible){ // if ((phys = right->getBlockSafe(lockedChunk, c - CHUNK_WIDTH + 1).caIndex) > -1) { // right->addPhysicsUpdate(phys, c - CHUNK_WIDTH + 1); // } //} else{ // return; //} //if (pos.z > 0){ //back // if ((phys = chunk->getBlockSafe(lockedChunk, c - CHUNK_WIDTH).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c - CHUNK_WIDTH); // } //} else if (back && back->isAccessible){ // if ((phys = back->getBlockSafe(lockedChunk, c + CHUNK_LAYER - CHUNK_WIDTH).caIndex) > -1) { // back->addPhysicsUpdate(phys, c + CHUNK_LAYER - CHUNK_WIDTH); // } //} else{ // return; //} //if (pos.z < CHUNK_WIDTH - 1){ //front // if ((phys = chunk->getBlockSafe(lockedChunk, c + CHUNK_WIDTH).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c + CHUNK_WIDTH); // } //} else if (front && front->isAccessible){ // if ((phys = front->getBlockSafe(lockedChunk, c - CHUNK_LAYER + CHUNK_WIDTH).caIndex) > -1) { // front->addPhysicsUpdate(phys, c - CHUNK_LAYER + CHUNK_WIDTH); // } //} else{ // return; //} //if (pos.y > 0){ //bottom // if ((phys = chunk->getBlockSafe(lockedChunk, c - CHUNK_LAYER).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c - CHUNK_LAYER); // } //} else if (bottom && bottom->isAccessible){ // if ((phys = bottom->getBlockSafe(lockedChunk, CHUNK_SIZE - CHUNK_LAYER + c).caIndex) > -1) { // bottom->addPhysicsUpdate(phys, CHUNK_SIZE - CHUNK_LAYER + c); // } //} else{ // return; //} //if (pos.y < CHUNK_WIDTH - 1){ //top // if ((phys = chunk->getBlockSafe(lockedChunk, c + CHUNK_LAYER).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c + CHUNK_LAYER); // } //} else if (top && top->isAccessible){ // if ((phys = top->getBlockSafe(lockedChunk, c - CHUNK_SIZE + CHUNK_LAYER).caIndex) > -1) { // top->addPhysicsUpdate(phys, c - CHUNK_SIZE + CHUNK_LAYER); // } //} } void ChunkUpdater::snowAddBlockToUpdateList(Chunk* chunk VORB_UNUSED, int c VORB_UNUSED) { //int phys; //const i32v3 pos = getPosFromBlockIndex(c); //if ((phys = chunk->getBlock(c).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c); //} //if (pos.y > 0){ //bottom // if ((phys = chunk->getBlock(c - CHUNK_LAYER).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c - CHUNK_LAYER); // } //} else if (chunk->bottom && chunk->bottom->isAccessible){ // if ((phys = chunk->bottom->getBlock(CHUNK_SIZE - CHUNK_LAYER + c).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, CHUNK_SIZE - CHUNK_LAYER + c); // } //} else{ // return; //} //if (pos.y < CHUNK_WIDTH - 1){ //top // if ((phys = chunk->getBlock(c + CHUNK_LAYER).caIndex) > -1) { // chunk->addPhysicsUpdate(phys, c + CHUNK_LAYER); // } //} else if (chunk->top && chunk->top->isAccessible){ // if ((phys = chunk->top->getBlock(c - CHUNK_SIZE + CHUNK_LAYER).caIndex) > -1) { // chunk->top->addPhysicsUpdate(phys, c - CHUNK_SIZE + CHUNK_LAYER); // } //} } //TODO: Replace this with simple emitterOnBreak //This function name is misleading, ignore for now void ChunkUpdater::breakBlock(Chunk* chunk VORB_UNUSED, int x VORB_UNUSED, int y VORB_UNUSED, int z VORB_UNUSED, int blockType VORB_UNUSED, double force VORB_UNUSED, f32v3 extraForce VORB_UNUSED) { // f32v4 color; // int btype = GETBLOCKID(blockType); // GLuint flags = GETFLAGS(blockType); // // color.a = 255; // // if (Blocks[btype].altColors.size() >= flags && flags){ // color.r = Blocks[btype].altColors[flags - 1].r; // color.g = Blocks[btype].altColors[flags - 1].g; // color.b = Blocks[btype].altColors[flags - 1].b; // // cout << btype << " " << flags-1 << " "; // } else{ // color.r = Blocks[btype].color.r; // color.g = Blocks[btype].color.g; // color.b = Blocks[btype].color.b; // } // // if (Blocks[btype].meshType != MeshType::NONE && Blocks[btype].explosivePower == 0){ // if (!chunk->mesh || chunk->mesh->inFrustum){ //// particleEngine.addParticles(BPARTICLES, f64v3(chunk->gridPosition.x + x, chunk->gridPosition.y + y, chunk->gridPosition.z + z), 0, 0.1, 0, 1, color, Blocks[btype].base.px, 2.0f, 4, extraForce); // } // } } // TODO(Ben): Make this cleaner void ChunkUpdater::placeFlora(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, int blockID VORB_UNUSED) { // //// Start it out at -1 so when we add 1 we get 0. //ui16 floraHeight = -1; //ui16 floraYpos = -1; //ui16 tertiaryData; //// Get tertiary data //if (blockIndex > CHUNK_LAYER) { // // Only need the data if its the same plant as we are // if (chunk->getBlockID(blockIndex - CHUNK_LAYER) == blockID) { // tertiaryData = chunk->getTertiaryData(blockIndex - CHUNK_LAYER); // // Grab height and position // floraHeight = VoxelBits::getFloraHeight(tertiaryData); // floraYpos = VoxelBits::getFloraPosition(tertiaryData); // } //} else { // if (chunk->bottom && chunk->bottom->isAccessible) { // // Only need the data if its the same plant as we are // if (chunk->bottom->getBlockID(blockIndex - CHUNK_LAYER + CHUNK_SIZE) == blockIndex) { // tertiaryData = chunk->bottom->getTertiaryData(blockIndex - CHUNK_LAYER + CHUNK_SIZE); // // Grab height and position // floraHeight = VoxelBits::getFloraHeight(tertiaryData); // floraYpos = VoxelBits::getFloraPosition(tertiaryData); // } // } else { // return; // } //} //tertiaryData = 0; //floraHeight += 1; // add one since we are bigger now //// Add 1 to the tertiary data //VoxelBits::setFloraHeight(tertiaryData, floraHeight); //VoxelBits::setFloraPosition(tertiaryData, floraYpos + 1); //// Set it //chunk->setTertiaryData(blockIndex, tertiaryData); //// Loop downwards through all flora blocks of the same type and increase their height by 1 //while (true) { // // Move the index down // if (blockIndex >= CHUNK_LAYER) { // blockIndex -= CHUNK_LAYER; // } else { // if (chunk->bottom && chunk->bottom->isAccessible) { // chunk = chunk->bottom; // } else { // return; // } // blockIndex = blockIndex - CHUNK_LAYER + CHUNK_SIZE; // } // // Loop while this is the same block type // if (chunk->getBlockID(blockIndex) == blockID) { // tertiaryData = chunk->getTertiaryData(blockIndex); // // Set new flora height // VoxelBits::setFloraHeight(tertiaryData, floraHeight); // chunk->setTertiaryData(blockIndex, tertiaryData); // } else { // return; // } //} } void ChunkUpdater::removeFlora(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED, int blockID VORB_UNUSED) { //// Grab tertiary data //ui16 tertiaryData = chunk->getTertiaryData(blockIndex); //// Grab height and position //ui16 floraYpos = VoxelBits::getFloraPosition(tertiaryData); //// Set tertiary data to 0 //chunk->setTertiaryData(blockIndex, 0); //// Recursively kill above flora blocks //blockIndex += CHUNK_LAYER; //if (blockIndex < CHUNK_SIZE) { // if (chunk->getBlockID(blockIndex) == blockID) { // removeBlockSafe(chunkManager, physicsEngine, chunk, lockedChunk, blockIndex, true); // } //} else if (chunk->top && chunk->top->isAccessible) { // blockIndex -= CHUNK_SIZE; // if (chunk->top->getBlockID(blockIndex) == blockID) { // removeBlockSafe(chunkManager, physicsEngine, chunk->top, lockedChunk, blockIndex, true); // } //} } float ChunkUpdater::getBurnProbability(Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED) { //float flammability = 0.0f; //// Bottom //int bt = vvox::getBottomBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //// Left //bt = vvox::getLeftBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //// Right //bt = vvox::getRightBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //// Back //bt = vvox::getBackBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //// Front //bt = vvox::getFrontBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //// Top //bt = vvox::getTopBlockData(chunk, lockedChunk, blockIndex); //if (bt == -1) return 0.0f; //flammability += GETBLOCK(bt).flammability; //if (flammability < 0) return 0.0f; //return flammability / 6.0f; return 0.0f; } void ChunkUpdater::updateFireBlock(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED) { ////left //int blockIndex2, blockIndex3, blockIndex4; //Chunk *owner2, *owner3, *owner4; //int bt; //const i32v3 pos = getPosFromBlockIndex(blockIndex); //const f32 sideTopMult = 1.5f; //const f32 topMult = 2.0f; //const f32 sideBotMult = 0.5f; //const f32 botMult = 0.8f; //Chunk* lockedChunk = nullptr; //burnAdjacentBlocks(chunkManager, physicsEngine, chunk, lockedChunk, blockIndex); ////********************************************************left //bt = vvox::getLeftBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2); ////left front //bt = vvox::getFrontBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3); ////left back //bt = vvox::getBackBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3); ////left top //bt = vvox::getTopBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////left top front //bt = vvox::getFrontBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideTopMult); ////left top back //bt = vvox::getBackBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideTopMult); ////left bottom //bt = vvox::getBottomBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); ////left bottom front //bt = vvox::getFrontBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideBotMult); ////left bottom back //bt = vvox::getBackBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideBotMult); ////********************************************************right //bt = vvox::getRightBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2); ////left front //bt = vvox::getFrontBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3); ////left back //bt = vvox::getBackBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3); ////left top //bt = vvox::getTopBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////left top front //bt = vvox::getFrontBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideTopMult); ////left top back //bt = vvox::getBackBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideTopMult); ////left bottom //bt = vvox::getBottomBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); ////left bottom front //bt = vvox::getFrontBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideBotMult); ////left bottom back //bt = vvox::getBackBlockData(owner3, lockedChunk, blockIndex3, blockIndex4, owner4); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex4, lockedChunk, bt, owner4, sideBotMult); ////******************************************************front //bt = vvox::getFrontBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2); ////front top //bt = vvox::getTopBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////front bottom //bt = vvox::getBottomBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); ////********************************************************back //bt = vvox::getBackBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2); ////back top //bt = vvox::getTopBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////back bottom //bt = vvox::getBottomBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); ////********************************************************top //bt = vvox::getTopBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2, topMult); ////top front //bt = vvox::getFrontBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////top back //bt = vvox::getBackBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideTopMult); ////********************************************************bottom //bt = vvox::getBottomBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner2); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex2, lockedChunk, bt, owner2, botMult); ////bottom front //bt = vvox::getFrontBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); ////bottom back //bt = vvox::getBackBlockData(owner2, lockedChunk, blockIndex2, blockIndex3, owner3); //if (bt == -1) { if (lockedChunk) { lockedChunk->unlock(); }; return; } //checkBurnBlock(blockIndex3, lockedChunk, bt, owner3, sideBotMult); //removeBlockSafe(chunkManager, physicsEngine, chunk, lockedChunk, blockIndex, false); //if (lockedChunk) lockedChunk->unlock(); } void ChunkUpdater::burnAdjacentBlocks(ChunkManager* chunkManager VORB_UNUSED, PhysicsEngine* physicsEngine VORB_UNUSED, Chunk* chunk VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockIndex VORB_UNUSED){ //int blockIndex2; //Chunk *owner2; //Block *b; //const i32v3 pos = getPosFromBlockIndex(blockIndex); //int bt = vvox::getBottomBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} ////left //bt = vvox::getLeftBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} ////right //bt = vvox::getRightBlockData(chunk, lockedChunk, blockIndex, pos.x, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} ////back //bt = vvox::getBackBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} ////front //bt = vvox::getFrontBlockData(chunk, lockedChunk, blockIndex, pos.z, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} ////top //bt = vvox::getTopBlockData(chunk, lockedChunk, blockIndex, pos.y, blockIndex2, owner2); //b = &(GETBLOCK(bt)); //if (b->flammability){ // if (b->burnTransformID == NONE){ // removeBlockSafe(chunkManager, physicsEngine, owner2, lockedChunk, blockIndex2, true); // } else{ // if (Blocks[b->burnTransformID].emitter){ // particleEngine.addEmitter(Blocks[b->burnTransformID].emitter, f64v3(owner2->voxelPosition.x + blockIndex2%CHUNK_WIDTH, owner2->voxelPosition.y + blockIndex2 / CHUNK_LAYER, owner2->voxelPosition.z + (blockIndex2%CHUNK_LAYER) / CHUNK_WIDTH), b->burnTransformID); // } // owner2->setBlockDataSafe(lockedChunk, blockIndex2, b->burnTransformID); // } // owner2->changeState(ChunkStates::MESH); //} } void ChunkUpdater::checkBurnBlock(int blockIndex VORB_UNUSED, Chunk*& lockedChunk VORB_UNUSED, int blockType VORB_UNUSED, Chunk *owner VORB_UNUSED, float burnMult VORB_UNUSED) { /* float burnProb; if ((blockType == NONE || GETBLOCK(blockType).waterBreak)){ burnProb = getBurnProbability(owner, lockedChunk, blockIndex) * burnMult; if (burnProb > 0){ float r = rand() / (float)RAND_MAX; if (r <= burnProb){ placeBlockSafe(owner, lockedChunk, blockIndex, FIRE); } } }*/ } ================================================ FILE: SoA/ChunkUpdater.h ================================================ #pragma once #include "Constants.h" // TODO(Ben): Temporary #include "BlockData.h" #include "Chunk.h" #include "VoxelUpdateBufferer.h" class PhysicsEngine; class ChunkManager; class BlockPack; enum class ChunkStates; class ChunkUpdater { public: static void randomBlockUpdates(PhysicsEngine* physicsEngine, Chunk* chunk); static void placeBlock(VoxelUpdateBufferer& bufferer, Chunk* chunk, Chunk*& lockedChunk VORB_UNUSED, BlockIndex blockIndex, BlockID blockData) { updateBlockAndNeighbors(bufferer, chunk, blockIndex, blockData); // TODO: Is this call needed? If so, reimplement and remove VORB_UNUSED tags. //addBlockToUpdateList(chunk, lockedChunk, blockIndex); } static void placeBlockSafe(Chunk* chunk, Chunk*& lockedChunk, BlockIndex blockIndex, BlockID blockData); static void placeBlockNoUpdate(Chunk* chunk, BlockIndex blockIndex, BlockID blockType); static void placeBlockFromLiquidPhysics(Chunk* chunk, Chunk*& lockedChunk, int blockIndex, int blockType); static void placeBlockFromLiquidPhysicsSafe(Chunk* chunk, Chunk*& lockedChunk, int blockIndex, int blockType); static void removeBlock(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, Chunk*& lockedChunk, int blockIndex, bool isBreak, double force = 0.0, f32v3 explodeDir = f32v3(0.0f)); static void removeBlockSafe(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, Chunk*& lockedChunk, int blockIndex, bool isBreak, double force = 0.0, f32v3 explodeDir = f32v3(0.0f)); static void removeBlockFromLiquidPhysics(Chunk* chunk, Chunk*& lockedChunk, int blockIndex); static void removeBlockFromLiquidPhysicsSafe(Chunk* chunk, Chunk*& lockedChunk, int blockIndex); static void updateNeighborStates(Chunk* chunk, const i32v3& pos, ChunkStates state); static void updateNeighborStates(Chunk* chunk, int blockID, ChunkStates state); // Assumes chunk is already locked. static void updateBlockAndNeighbors(VoxelUpdateBufferer& bufferer, Chunk* chunk, BlockIndex index); static void updateBlockAndNeighbors(VoxelUpdateBufferer& bufferer, Chunk* chunk, BlockIndex index, BlockID id); static void snowAddBlockToUpdateList(Chunk* chunk, int c); static BlockPack* blockPack; private: //TODO: Replace with emitterOnBreak static void breakBlock(Chunk* chunk, int x, int y, int z, int blockType, double force = 0.0f, f32v3 extraForce = f32v3(0.0f)); static void placeFlora(Chunk* chunk, int blockIndex, int blockID); static void removeFlora(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, Chunk*& lockedChunk, int blockIndex, int blockID); //Fire static void updateFireBlock(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, int blockIndex); static float getBurnProbability(Chunk* chunk, Chunk*& lockedChunk, int blockIndex); static void burnAdjacentBlocks(ChunkManager* chunkManager, PhysicsEngine* physicsEngine, Chunk* chunk, Chunk*& lockedChunk, int blockIndex); static inline void checkBurnBlock(int blockIndex, Chunk*& lockedChunk, int blockType, Chunk *owner, float burnMult = 1.0); }; ================================================ FILE: SoA/ClientState.h ================================================ // // ClientState.h // Seed of Andromeda // // Created by Benjamin Arnold on 11 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // State for the client only. // #pragma once #ifndef ClientState_h__ #define ClientState_h__ #include "BlockTextureLoader.h" #include "BlockTexturePack.h" #include "Camera.h" #include "MainMenuSystemViewer.h" #include "ModPathResolver.h" #include "VoxelEditor.h" class DebugRenderer; class ChunkMeshManager; struct ClientState { ChunkMeshManager* chunkMeshManager = nullptr; // TODO(Ben): Commonstate DebugRenderer* debugRenderer = nullptr; MainMenuSystemViewer* systemViewer = nullptr; BlockTextureLoader blockTextureLoader; BlockTexturePack* blockTextures = nullptr; ModPathResolver texturePathResolver; VoxelEditor voxelEditor; // TODO(Ben): Should maybe be server side? vecs::EntityID startingPlanet = 0; vecs::EntityID playerEntity = 0; // TODO(Ben): This is temporary! CinematicCamera spaceCamera; ///< The camera that looks at the planet from space bool isNewGame = true; f64v3 startSpacePos = f64v3(0.0f); ///< Starting position of player entity }; #endif // ClientState_h__ ================================================ FILE: SoA/CloseTerrainPatch.cpp ================================================ #include "stdafx.h" #include "TerrainPatch.h" #include #include "BlockData.h" #include "Chunk.h" #include "FloraGenerator.h" #include "GameManager.h" //#include "Options.h" #include "Planet.h" #include "WorldStructs.h" inline double BilinearInterpolation(int &a, int &b, int &c, int &d, int &step, float &x, float &z) { double px, pz; px = ((double)(x)) / step; pz = ((double)(z)) / step; return (a)*(1 - px)*(1 - pz) + (b)*px*(1 - pz) + (c)*(1 - px)*pz + (d)*px*pz; } CloseTerrainPatch::CloseTerrainPatch(int lodWidth){ for (int i = 0; i < 4; i++){ lods[i] = NULL; } width = lodWidth; vboID = 0; vaoID = 0; vboIndexID = 0; updateVecIndex = -1; vecIndex = -1; treeVboID = 0; treeIndexSize = 0; lodMap = NULL; } CloseTerrainPatch::~CloseTerrainPatch(){ ClearLODMap(); ClearBuffers(); DeleteChildren(); } void CloseTerrainPatch::ClearLODMap() { if (lodMap != NULL){ delete[] lodMap; lodMap = NULL; } } void CloseTerrainPatch::ClearBuffers() { indexSize = 0; if (vboID != 0){ glDeleteVertexArrays(1, &vaoID); glDeleteBuffers(1, &vboID); vaoID = 0; vboID = 0; } if (vboIndexID != 0){ glDeleteBuffers(1, &vboIndexID); vboIndexID = 0; } ClearTreeBuffers(); } void CloseTerrainPatch::ClearTreeBuffers() { treeIndexSize = 0; if (treeVboID != 0){ glDeleteBuffers(1, &treeVboID); treeVboID = 0; } } void CloseTerrainPatch::DeleteChildren() { if (lods[0]){ for (unsigned int i = 0; i < 4; i++){ lods[i]->ClearBuffers(); lods[i]->DeleteChildren(); delete lods[i]; //calls delete children lods[i] = NULL; } } } //itinializes the LOD and computes distance void CloseTerrainPatch::Initialize(int x, int y, int z, int wx, int wy, int wz, CloseTerrainPatch *Parent, int ChildNum, int initialDetailLevel) { childNum = ChildNum; waitForMesh = 0; vboID = 0; vaoID = 0; treeVboID = 0; drawTrees = 0; lodMap = NULL; vboIndexID = 0; for (unsigned int i = 0; i < 4; i++){ lods[i] = NULL; } indexSize = 0; treeIndexSize = 0; parent = Parent; updateCode = 1; vecIndex = -1; updateVecIndex = -1; hasSkirt = 1; hasBoundingBox = 0; cullRadius = 0; hasProperDist = 0; X = x; Y = y; Z = z; double vx = X + width / 2; double vy = Y + width / 2; double vz = Z + width / 2; //double tmph = currTerrainGenerator->GenerateSingleHeight(vx, vy, vz); double magnitude = sqrt(vx*vx + vy*vy + vz*vz); vx /= magnitude; vy /= magnitude; vz /= magnitude; closestPoint.x = X + width/2; closestPoint.y = Y + width / 2; closestPoint.z = Z + width / 2; double dx = (double)(X - wx); double dy = (double)(Y - wy); double dz = (double)(Z - wz); //approximate distance, not true distance until mesh has been made initially distance = sqrt(dx*dx + dy*dy + dz*dz) - width*0.5*1.4142; if (distance < 0) distance = 0; hasMesh = 0; if (initialDetailLevel != -1){ detailLevel = initialDetailLevel; step = lodDetailLevels[detailLevel + graphicsOptions.lodDetail]; waitForMesh = 1; } else{ CalculateDetailLevel(distance, 0); } if (detailLevel == 0) hasSkirt = 0; if ((width / step) > maxVertexWidth){ CreateChildren(wx, wy, wz); } } void CloseTerrainPatch::Draw(glm::dvec3 &PlayerPos, glm::dvec3 &rotPlayerPos, glm::mat4 &VP, GLuint mvpID, GLuint worldOffsetID, bool onPlanet) { // if (indexSize){ // if (distance < closestTerrainPatchDistance) closestTerrainPatchDistance = distance; // if (SphereInFrustum((float)(boundingBox.x / 2 - rotPlayerPos.x), (float)(boundingBox.y / 2 - rotPlayerPos.y), (float)(boundingBox.z / 2 - rotPlayerPos.z), (float)cullRadius, worldFrustum)){ // // glm::mat4 MVP; // // GlobalModelMatrix[3][0] = ((float)((double)X - PlayerPos.x)); // GlobalModelMatrix[3][1] = ((float)((double)Y - PlayerPos.y)); // GlobalModelMatrix[3][2] = ((float)((double)Z - PlayerPos.z)); // MVP = VP * GlobalModelMatrix; // // glUniformMatrix4fv(mvpID, 1, GL_FALSE, &MVP[0][0]); //some kind of crash here :/ // //// glUniform3f(worldOffsetID, drawX, drawY, drawZ); // // // // glBindBuffer(GL_ARRAY_BUFFER, vboID); // // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexID); // // // glBindVertexArray(vaoID); // glDrawElements(GL_TRIANGLES, indexSize, GL_UNSIGNED_SHORT, 0); // glBindVertexArray(0); // } // } // else if (lods[0]){ // lods[0]->Draw(PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); // lods[1]->Draw(PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); // lods[2]->Draw(PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); // lods[3]->Draw(PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); // } } void CloseTerrainPatch::DrawTrees(glm::dvec3 &PlayerPos, glm::mat4 &VP) { //if (treeIndexSize){ // if (SphereInFrustum((float)(worldX + boundingBox.x / 2 - PlayerPos.x), (float)(worldY + boundingBox.y / 2 - PlayerPos.y), (float)(worldZ + boundingBox.z / 2 - PlayerPos.z), (float)cullRadius) // && CheckHorizon(PlayerPos)){ // GlobalModelMatrix[3][0] = ((float)((double)drawX - PlayerPos.x)); // GlobalModelMatrix[3][1] = ((float)((double)drawY - PlayerPos.y)); // GlobalModelMatrix[3][2] = ((float)((double)drawZ - PlayerPos.z)); // glm::mat4 MVP = VP * GlobalModelMatrix; // glUniformMatrix4fv(treeShader.mvpID, 1, GL_FALSE, &MVP[0][0]); // glUniformMatrix4fv(treeShader.mID, 1, GL_FALSE, &GlobalModelMatrix[0][0]); // //glUniformMatrix4fv(treeShader.mvpID, 1, GL_FALSE, &MVP[0][0]); // //glUniformMatrix4fv(treeShader.gVPID, 1, GL_FALSE, &VP[0][0]); // //glUniform3f(treeShader.upID, worldNormal.x, worldNormal.y, worldNormal.z); // totVertices += treeIndexSize; // totDrawCalls++; // glBindBuffer(GL_ARRAY_BUFFER, treeVboID); // //position // glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TreeVertex), 0); // //particle center // glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TreeVertex), ((char *)NULL + (8))); // //leaf color and size // glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TreeVertex), ((char *)NULL + (20))); // //trunk color and ltex // glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TreeVertex), ((char *)NULL + (24))); // glDrawElements(GL_TRIANGLES, treeIndexSize, GL_UNSIGNED_INT, NULL); // } //} //else if (lods[0]){ // lods[0]->DrawTrees(PlayerPos, VP); // lods[1]->DrawTrees(PlayerPos, VP); // lods[2]->DrawTrees(PlayerPos, VP); // lods[3]->DrawTrees(PlayerPos, VP); //} } const bool colorDebug = 0; bool CloseTerrainPatch::CreateMesh() { return 1; } void CloseTerrainPatch::CreateChildren(int wx, int wy, int wz, int initialDetail) { int hw = width / 2; lods[0] = new CloseTerrainPatch(hw); lods[1] = new CloseTerrainPatch(hw); lods[2] = new CloseTerrainPatch(hw); lods[3] = new CloseTerrainPatch(hw); lods[0]->Initialize(X, 0, Z, wx, wy, wz, this, 0, initialDetail); lods[1]->Initialize(X + hw, 0, Z, wx, wy, wz, this, 1, initialDetail); lods[2]->Initialize(X, 0, Z + hw, wx, wy, wz, this, 2, initialDetail); lods[3]->Initialize(X + hw, 0, Z + hw, wx, wy, wz, this, 3, initialDetail); /*switch (face){ case P_TOP: lods[0]->Initialize(X, scaledRadius, Z, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(X + hw, scaledRadius, Z, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(X, scaledRadius, Z + hw, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(X + hw, scaledRadius, Z + hw, wx, wy, wz, radius, face, this, 3, initialDetail); break; case P_LEFT: lods[0]->Initialize(-scaledRadius, Y, Z, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(-scaledRadius, Y, Z + hw, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(-scaledRadius, Y + hw, Z, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(-scaledRadius, Y + hw, Z + hw, wx, wy, wz, radius, face, this, 3, initialDetail); break; case P_RIGHT: lods[0]->Initialize(scaledRadius, Y, Z, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(scaledRadius, Y, Z + hw, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(scaledRadius, Y + hw, Z, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(scaledRadius, Y + hw, Z + hw, wx, wy, wz, radius, face, this, 3, initialDetail); break; case P_FRONT: lods[0]->Initialize(X, Y, scaledRadius, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(X + hw, Y, scaledRadius, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(X, Y + hw, scaledRadius, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(X + hw, Y + hw, scaledRadius, wx, wy, wz, radius, face, this, 3, initialDetail); break; case P_BACK: lods[0]->Initialize(X, Y, -scaledRadius, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(X + hw, Y, -scaledRadius, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(X, Y + hw, -scaledRadius, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(X + hw, Y + hw, -scaledRadius, wx, wy, wz, radius, face, this, 3, initialDetail); break; case P_BOTTOM: lods[0]->Initialize(X, -scaledRadius, Z, wx, wy, wz, radius, face, this, 0, initialDetail); lods[1]->Initialize(X + hw, -scaledRadius, Z, wx, wy, wz, radius, face, this, 1, initialDetail); lods[2]->Initialize(X, -scaledRadius, Z + hw, wx, wy, wz, radius, face, this, 2, initialDetail); lods[3]->Initialize(X + hw, -scaledRadius, Z + hw, wx, wy, wz, radius, face, this, 3, initialDetail); break; }*/ } //returns the update code 0 = no update, 1 = update, 2 = remove children and update int CloseTerrainPatch::update(int wx, int wy, int wz) { int rv = 0; if (lods[0]){ for (unsigned int i = 0; i < 4; i++){ if (lods[i]->update(wx, wy, wz)) rv = 1; //if a child needs to change its mesh, we will return 1 } SortChildren(); //sort children for better mesh updates } double dx, dy, dz; int oldDetailLevel = detailLevel; int oldStep = step; if (hasBoundingBox){ //bounding box distance closestPoint.x = (wx <= X) ? X : ((wx > X + boundingBox.x) ? (X + boundingBox.x) : wx); closestPoint.y = (wy <= Y) ? Y : ((wy > Y + boundingBox.y) ? (Y + boundingBox.y) : wy); closestPoint.z = (wz <= Z) ? Z : ((wz > Z + boundingBox.z) ? (Z + boundingBox.z) : wz); dx = (double)(closestPoint.x) - wx; dy = (double)(closestPoint.y) - wy; dz = (double)(closestPoint.z) - wz; distance = sqrt(dx*dx + dy*dy + dz*dz); if (hasProperDist){ CalculateDetailLevel(distance, 100); } else{ CalculateDetailLevel(distance, 0); hasProperDist = 1; } } else{ //approximate distance closestPoint.x = X; closestPoint.y = Y; closestPoint.z = Z; dx = (double)(X)-wx; dy = (double)(Y)-wy; dz = (double)(Z)-wz; distance = sqrt(dx*dx + dy*dy + dz*dz) - width*0.5*1.4142; if (distance < 0) distance = 0; if (waitForMesh == 0) CalculateDetailLevel(distance, 0); //shorten the distance by the diagonal radius of an LOD } if (detailLevel == 0){ //if detail is lowest, we dont need to display a skirt hasSkirt = 0; } else{ hasSkirt = 1; } //if the detail changed, then we need to reload the mesh. if (detailLevel != oldDetailLevel || step != oldStep){ // cout << detailLevel << " " << oldDetailLevel << " " << (width / step) << " " << width << " " << step << endl; //if there will be too many vertices for this size of LOD, split it into 4 children if ((width / step) > maxVertexWidth){ //if we already have children, we simply return 1 to indicate a needed update. if (lods[0]){ updateCode = 1; return 1; //update Children } CreateChildren(wx, wy, wz, oldDetailLevel); updateCode = 1; return 1; } else if (lods[0]){ //if we have children but no longer need them, flag for deletion updateCode = 2; return 2; //remove children } updateCode = 1; return 1;//push onto update list } if (rv){ updateCode = 1; } return rv; } void CloseTerrainPatch::CalculateDetailLevel(double dist, int threshold) { for (int i = 0; i < DetailLevels; i++){ //check if the distance is inside the range for the different detail levels if (dist < lodDistanceLevels[i] - threshold && (i == 0 || dist >= lodDistanceLevels[i - 1] + threshold)){ //set the step for meshing and store the new detail level step = lodDetailLevels[i + graphicsOptions.lodDetail]; detailLevel = i; break; } } } void CloseTerrainPatch::SortChildren() { CloseTerrainPatch *temp; int j; for (unsigned int i = 1; i < 4; i++) { temp = lods[i]; for (j = i - 1; (j >= 0) && (temp->distance < lods[j]->distance); j--) { lods[j + 1] = lods[j]; lods[j + 1]->vecIndex = j + 1; } lods[j + 1] = temp; lods[j + 1]->vecIndex = j + 1; } } ================================================ FILE: SoA/CloudsComponentRenderer.cpp ================================================ #include "stdafx.h" #include "CloudsComponentRenderer.h" #include "SpaceSystem.h" #include "RenderUtils.h" #include "soaUtils.h" #include "ShaderLoader.h" #include #include #include #include CloudsComponentRenderer::~CloudsComponentRenderer() { dispose(); } void CloudsComponentRenderer::initGL() { if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/CloudsShading/Clouds.vert", "Shaders/CloudsShading/Clouds.frag"); } if (!m_icoVbo) buildMesh(); } void CloudsComponentRenderer::draw(const CloudsComponent& cCmp, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent VORB_MAYBE_UNUSED, const AxisRotationComponent& arComponent, const AtmosphereComponent& aCmp) { m_program.use(); f64q invOrientation = glm::inverse(arComponent.currentOrientation); const f32v3 rotpos(invOrientation * f64v3(relCamPos)); const f32v3 rotLightDir = f32v3(invOrientation * f64v3(lightDir)); // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)arComponent.currentOrientation.x; orientationF32.y = (f32)arComponent.currentOrientation.y; orientationF32.z = (f32)arComponent.currentOrientation.z; orientationF32.w = (f32)arComponent.currentOrientation.w; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); // Create WVP matrix f32m4 WVP(1.0); setMatrixScale(WVP, f32v3(cCmp.height + cCmp.planetRadius)); setMatrixTranslation(WVP, -relCamPos); WVP = VP * WVP * rotationMatrix; // Set uniforms glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &WVP[0][0]); static f32 time = 0.0; time += 0.001f; glUniform1f(m_program.getUniform("unTime"), time); glUniform3fv(m_program.getUniform("unColor"), 1, &cCmp.color[0]); glUniform3fv(m_program.getUniform("unNoiseScale"), 1, &cCmp.scale[0]); glUniform1f(m_program.getUniform("unDensity"), cCmp.density); glUniform1f(m_program.getUniform("unCloudsRadius"), cCmp.planetRadius + cCmp.height); // For logarithmic Z buffer glUniform1f(m_program.getUniform("unZCoef"), zCoef); // Scattering f32 camHeight = glm::length(rotpos); // f32 camHeight2 = camHeight * camHeight; glUniform3fv(m_program.getUniform("unLightDirWorld"), 1, &rotLightDir[0]); glUniform3fv(m_program.getUniform("unCameraPos"), 1, &rotpos[0]); glUniform3fv(m_program.getUniform("unInvWavelength"), 1, &aCmp.invWavelength4[0]); glUniform1f(m_program.getUniform("unCameraHeight2"), camHeight * camHeight); glUniform1f(m_program.getUniform("unOuterRadius"), aCmp.radius); glUniform1f(m_program.getUniform("unOuterRadius2"), aCmp.radius * aCmp.radius); glUniform1f(m_program.getUniform("unInnerRadius"), aCmp.planetRadius); glUniform1f(m_program.getUniform("unKrESun"), aCmp.kr * aCmp.esun); glUniform1f(m_program.getUniform("unKmESun"), aCmp.km * aCmp.esun); glUniform1f(m_program.getUniform("unKr4PI"), (f32)(aCmp.kr * M_4_PI)); glUniform1f(m_program.getUniform("unKm4PI"), (f32)(aCmp.km * M_4_PI)); float scale = 1.0f / (aCmp.radius - aCmp.planetRadius); glUniform1f(m_program.getUniform("unScale"), scale); glUniform1f(m_program.getUniform("unScaleDepth"), aCmp.scaleDepth); glUniform1f(m_program.getUniform("unScaleOverScaleDepth"), scale / aCmp.scaleDepth); glUniform1i(m_program.getUniform("unNumSamples"), 3); glUniform1f(m_program.getUniform("unNumSamplesF"), 3.0f); glUniform1f(m_program.getUniform("unG"), aCmp.g); glUniform1f(m_program.getUniform("unG2"), aCmp.g * aCmp.g); // Bind VAO glBindVertexArray(m_vao); // Render glDepthMask(GL_FALSE); if (camHeight > cCmp.planetRadius + cCmp.height) vg::RasterizerState::CULL_CLOCKWISE.set(); else vg::RasterizerState::CULL_COUNTER_CLOCKWISE.set(); glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, 0); glDepthMask(GL_TRUE); vg::RasterizerState::CULL_CLOCKWISE.set(); glBindVertexArray(0); m_program.unuse(); } void CloudsComponentRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); if (m_icoVbo) { vg::GpuMemory::freeBuffer(m_icoVbo); m_icoVbo = 0; } if (m_icoIbo) { vg::GpuMemory::freeBuffer(m_icoIbo); m_icoIbo = 0; } if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; } } void CloudsComponentRenderer::buildMesh() { std::vector indices; std::vector positions; vmesh::generateIcosphereMesh(3, indices, positions); m_numIndices = indices.size(); glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); vg::GpuMemory::createBuffer(m_icoVbo); vg::GpuMemory::createBuffer(m_icoIbo); vg::GpuMemory::bindBuffer(m_icoVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_icoVbo, vg::BufferTarget::ARRAY_BUFFER, positions.size() * sizeof(f32v3), positions.data(), vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(m_icoIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_icoIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(ui32), indices.data(), vg::BufferUsageHint::STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); m_program.enableVertexAttribArrays(); glBindVertexArray(0); } ================================================ FILE: SoA/CloudsComponentRenderer.h ================================================ #pragma once #include #include #include #include #include struct AtmosphereComponent; struct AxisRotationComponent; struct CloudsComponent; struct SpaceLightComponent; class CloudsComponentRenderer { public: ~CloudsComponentRenderer(); void initGL(); void draw(const CloudsComponent& cCmp, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent, const AxisRotationComponent& arComponent, const AtmosphereComponent& aCmp); void dispose(); private: void buildMesh(); vg::GLProgram m_program; VGBuffer m_icoVbo = 0; VGIndexBuffer m_icoIbo = 0; VGVertexArray m_vao = 0; int m_numIndices = 0; }; ================================================ FILE: SoA/Collision.cpp ================================================ #include "stdafx.h" #include "Collision.h" #include #include "Chunk.h" #include "BlockPack.h" void blockCollision(Player* player, Chunk* chunk, Chunk* lockedChunk, ui16 blockType, i32 c, f64 bdx, f64 bdy, f64 bdz, f64 dx, f64 dy, f64 dz); //This method could be easily implemented as a recursive function, but is more efficient if unfolded //TODO(Ben) This is laughable. void aabbChunkCollision(Player* player VORB_UNUSED, f64v3* playerPos VORB_UNUSED, Chunk* * chunks VORB_UNUSED, ui8 size VORB_UNUSED) { //int x, y, z, x1, y1, z1 ,x2, y2, z2, x3, y3, z3, x4, y4, z4, c; //midpoints //int blockID; //double x5, y5, z5; //only this one requires double precision //double yPosOffset; //double boxX, boxY, boxZ; //double dx, dy, dz, bdx, bdy, bdz; //NChunk* chunk; //f32v3 *playerBox = nullptr; // &(player->boundingBox); //boxX = playerBox->x; //boxY = playerBox->y/2.0; //the box point is dead center of the player //boxZ = playerBox->z; //yPosOffset = boxY; //since we are using a center point, we use yPosOffset to move the midpoint from the feet to the center //NChunk* lockedChunk = nullptr; //for (unsigned char i = 0; i < size; i++) { //loops through chunks // if (!(chunks[i]) || chunks[i]->isAccessible == false) continue; //avoid errors // //find the midpoint so that we can subdivide the chunk into 8 sections // x = chunks[i]->voxelPosition.x + CHUNK_WIDTH / 2; // y = chunks[i]->voxelPosition.y + CHUNK_WIDTH / 2; // z = chunks[i]->voxelPosition.z + CHUNK_WIDTH / 2; // //checks to see if the distance between the players midpoint and the box midpoint is greater than the radius // //all three axis must be colliding for a collision // if ((boxX + 16) < ABS(playerPos->x - x)) continue; // if ((boxY + 16) < ABS(playerPos->y + yPosOffset - y)) continue; // if ((boxZ + 16) < ABS(playerPos->z - z)) continue; // for (unsigned char l0 = 0; l0 < 2; l0++){ //zeroth subdivide // //loops through each of the smaller boxes, setting x1, y1, z1 to the midpoint // if (l0 == 0){ // y1 = y + 8; // }else{ // y1 = y - 8; // } // for (unsigned char d = 0; d < 4; d++){ // if (d == 0){ // x1 = x - 8; // z1 = z - 8; // }else if (d==1){ // x1 = x + 8; // z1 = z - 8; // }else if (d==2){ // x1 = x - 8; // z1 = z + 8; // }else if (d==3){ // x1 = x + 8; // z1 = z + 8; // } // //same thing, check if the player is inside the intersection // if ((boxX + 8) < ABS(playerPos->x - x1)) continue; // if ((boxY + 8) < ABS(playerPos->y + yPosOffset - y1)) continue; // if ((boxZ + 8) < ABS(playerPos->z - z1)) continue; // for (unsigned char l = 0; l < 2; l++){ //first subdivide // if (l == 0){ // y2 = y1 + 4; // }else{ // y2 = y1 - 4; // } // for (unsigned char j = 0; j < 4; j++){ // if (j == 0){ // x2 = x1 - 4; // z2 = z1 - 4; // }else if (j==1){ // x2 = x1 + 4; // z2 = z1 - 4; // }else if (j==2){ // x2 = x1 - 4; // z2 = z1 + 4; // }else if (j==3){ // x2 = x1 + 4; // z2 = z1 + 4; // } // if ((boxX + 4) < ABS(playerPos->x - x2)) continue; // if ((boxY + 4) < ABS(playerPos->y + yPosOffset - y2)) continue; // if ((boxZ + 4) < ABS(playerPos->z - z2)) continue; // for (unsigned char l2 = 0; l2 < 2; l2++){//second subdivide // if (l2 == 0){ // y3 = y2 + 2; // }else{ // y3 = y2 - 2; // } // for (unsigned char k = 0; k < 4; k++){ // if (k == 0){ // x3 = x2 - 2; // z3 = z2 - 2; // }else if (k==1){ // x3 = x2 + 2; // z3 = z2 - 2; // }else if (k==2){ // x3 = x2 - 2; // z3 = z2 + 2; // }else if (k==3){ // x3 = x2 + 2; // z3 = z2 + 2; // } // if ((boxX + 2) < ABS(playerPos->x - x3)) continue; // if ((boxY + 2) < ABS(playerPos->y + yPosOffset - y3)) continue; // if ((boxZ + 2) < ABS(playerPos->z - z3)) continue; // // for (unsigned char l3 = 0; l3 < 2; l3++){ //third subdivide // if (l3 == 0){ // y4 = y3 + 1; // }else{ // y4 = y3 - 1; // } // for (unsigned char m = 0; m < 4; m++){ // if (m == 0){ // x4 = x3 - 1; // z4 = z3 - 1; // }else if (m==1){ // x4 = x3 + 1; // z4 = z3 - 1; // }else if (m==2){ // x4 = x3 - 1; // z4 = z3 + 1; // }else if (m==3){ // x4 = x3 + 1; // z4 = z3 + 1; // } // if ((boxX + 1) < ABS(playerPos->x - x4)) continue; // if ((boxY + 1) < ABS(playerPos->y + yPosOffset - y4)) continue; // if ((boxZ + 1) < ABS(playerPos->z - z4)) continue; // for (unsigned char l4 = 0; l4 < 2; l4++){ //final subdivide. Check if the player is intersecting a block // if (l4 == 0){ // y5 = y4 + 0.5; // }else{ // y5 = y4 - 0.5; // } // for (unsigned char n = 0; n < 4; n++){ // if (n == 0){ // x5 = x4 - 0.5; // z5 = z4 - 0.5; // }else if (n==1){ // x5 = x4 + 0.5; // z5 = z4 - 0.5; // }else if (n==2){ // x5 = x4 - 0.5; // z5 = z4 + 0.5; // }else if (n==3){ // x5 = x4 + 0.5; // z5 = z4 + 0.5; // } // // //find the distance from the players midpoint to the block's midpoint. // dx = playerPos->x - x5; // dy = playerPos->y + yPosOffset - y5; // dz = playerPos->z - z5; // if ((boxX + 0.5) < ABS(dx)) continue; // if ((boxY + 0.5) < ABS(dy)) continue; // if ((boxZ + 0.5) < ABS(dz)) continue; // int blx, bly, blz; // bool moveUp = 1; // // //finds the block coordinate location in the chunk // double tmp = CHUNK_WIDTH/2.0 - 0.5; // blx = (int)(x5 - (x - tmp)); // bly = (int)(CHUNK_LAYER * (y5 - (y - tmp))); // blz = (int)(CHUNK_WIDTH * (z5 - (z - tmp))); // //finds the depth of collision. Higher value means deeper collision. Must reference dx dy and dz // //for direction. Substracts the length of both bounding boxes by the distance between to get the // //difference. // bdx = (double)(0.5+boxX) - ABS(dx); // bdy = (double)(0.5+boxY) - ABS(dy); // bdz = (double)(0.5+boxZ) - ABS(dz); // c = blx + bly + blz; // chunk = chunks[i]; // if (chunk->isAccessible == false) continue; // blockID = chunk->getBlockIDSafe(lockedChunk, c); // if (blockID){ // blockCollision(player, lockedChunk, chunks[i], blockID, c, bdx, bdy, bdz, dx, dy, dz); // } // } // } // } // } // } // } // } // } // } // } //} //if (lockedChunk) lockedChunk->unlock(); } // TODO(Ben): What the FUCK is this?!?! This code is rated XXX. void blockCollision(Player *player VORB_UNUSED, Chunk* chunk VORB_UNUSED, Chunk* lockedChunk VORB_UNUSED, GLushort blockType VORB_UNUSED, int c VORB_UNUSED, double bdx VORB_UNUSED, double bdy VORB_UNUSED, double bdz VORB_UNUSED, double dx VORB_UNUSED, double dy VORB_UNUSED, double dz VORB_UNUSED) { // if (chunks[i]->data[blx + bly + blz] == WATER) continue; //no clip water // f64v3 *playerPos = &(player->gridPosition); // double boxX = player->boundingBox.x; // double boxY = player->boundingBox.y/2.0; // double boxZ = player->boundingBox.z; // double stepMod = 1.0f/(float)PLAYER_COLLISION_STEPS; // bool pushedDown = 0; // CollisionData *cData = &(player->collisionData); // // if (blockType >= LOWWATER){ // // cout << (player->headPosition.y - player->position.y) << " " << dy-(blockType - LOWWATER)*0.005 << endl; // if (dy <= 0) player->isSwimming = 1; // // if (dy-(blockType - LOWWATER)*0.005 <= -(player->headPosition.y - player->position.y)) player->underWater = 1; // if (dy+0.5 < -(player->headPosition.y - player->gridPosition.y - boxY - (blockType - LOWWATER)*0.01)) player->isUnderWater = 1; // } // // //cout << dx << " " << dy << " " << dz << " " << bdx << " " << bdy << " " << bdz << endl; // if (Blocks[blockType].moveMod <= 1.0){ // if (Blocks[blockType].moveMod < player->getMoveMod()) player->setMoveMod(Blocks[blockType].moveMod); // } // else if (player->getMoveMod() >= 1.0){ //slippery things like ice // if (Blocks[blockType].moveMod > player->getMoveMod()) player->setMoveMod(Blocks[blockType].moveMod); // } // //player->moveMod *= pow(Blocks[blockType].moveMod, stepMod); // if (Blocks[blockType].collide == 0 || player->isFlying) return; //do no collision with the block // // double mov = 0.07 * glSpeedFactor * stepMod; //mov is the distance we should move from a collision // double push = 0.9 * stepMod;//how hard the block tries to push us out. Start with an almost full push. Dont set to 1.0 or it pushes too far and we climb jerkily // bool moveUp = 0; // bool collided = 0; // // if (dy >= 1.0) { //if the player is more than 1.0 block above a block // NChunk* own; // int nc; // int topc = vvox::getTopBlockData(chunk, lockedChunk, c, nc, own); // if (GETBLOCK(topc).collide == 0 && GETBLOCK(vvox::getTopBlockData(own, lockedChunk, nc)).collide == 0) { // if there is at least 2 free spaces above // // cout << "TOP: " << chunk->GetTopBlock(c) << " " << (int)GETBLOCK(chunk->GetTopBlock(c)).collide << " "; // moveUp = 1; // push = 0.1 * stepMod; //if its a low climb, we can sorta clip into it // } // }else if (dy > -2.0 && dy < 1.0){ // player->canCling = 1; // if (player->isSprinting) { //climbing happens when sprinting or holding jump // if (GETBLOCK(vvox::getTopBlockData(chunk, lockedChunk, c)).collide == 0) { // moveUp = 1; // player->isClinging = 1; // } // } // } // // if (player->isSprinting){ // mov = 0.1 * glSpeedFactor * stepMod; // } // // if (moveUp){ // if (bdy < mov){ //just a normal top of the block collision // if (bdy > cData->yMove) cData->yMove = bdy; // if (player->velocity.y < 0.0f){ // if (-player->velocity.y > cData->yDecel) cData->yDecel = -player->velocity.y; // player->velocity.y = 0.0f; // } // if (bdx > 0.2 && bdz > 0.2){ // player->isGrounded = 1; // } // }else{ //climbing collision // if (mov > cData->yMove) cData->yMove = mov; // if (player->velocity.y < 0.0f){ // if (-player->velocity.y > cData->yDecel) cData->yDecel = -player->velocity.y; // player->velocity.y = 0.0f; // } // player->isClimbing = 1; // } // } // // if (bdy < bdz && bdy < bdx && dy < -0.5 && Blocks[GETBLOCKID(vvox::getBottomBlockData(chunk, lockedChunk, c))].collide == 0) { //head y collision // //TODO PREVENT A FAST MOVING PERSON FROM GOING THROUGH BOTTOM BY TESTING HOW MUCH WE CAN CROUCH BEFORE WE BOUNCE OFF // // cout << "A"; // if (bdy > cData->headSquish) cData->headSquish = bdy; // if (player->velocity.y > 0.0f) player->velocity.y = 0.0; //maybe not do this? let people hit their heads and feet scrunch up // } // if (bdx < bdz && bdy > 0.2){ //x collision BDY is different when crouching. Look into this // if (!player->isSprinting && GETBLOCK(vvox::getBottomBlockData(chunk, lockedChunk, c)).collide == 0) { //auto crouch // if (player->getCrouch() != 1.0){ // if (dx < 0 && GETBLOCK(vvox::getLeftBlockData(chunk, lockedChunk, c)).collide == 0) { // pushedDown = 1; // } else if (dx > 0 && GETBLOCK(vvox::getRightBlockData(chunk, lockedChunk, c)).collide == 0) { // pushedDown = 1; // } // } // if (pushedDown && !player->isClinging){ // collided = 1; //to stop z from colliding // mov = 0.2 * glSpeedFactor * stepMod; // if (mov > bdy - 0.2) mov = bdy - 0.2; // if (mov > cData->headSquish) cData->headSquish = mov; // // cout << "G " << (int)player->clinging; // } // //if (player->velocity.y > 0.0f) player->velocity.y = 0.0; // } // if (!pushedDown || dy > -0.2){ // if (dx > 0 && GETBLOCK(vvox::getRightBlockData(chunk, lockedChunk, c)).collide == 0) { // mov = bdx*push; // if (mov > ABS(cData->xMove)) cData->xMove = mov; // collided = 1; // // cout << "C"; // } else if (GETBLOCK(vvox::getLeftBlockData(chunk, lockedChunk, c)).collide == 0) { // mov = bdx*push; // if (mov > ABS(cData->xMove)) cData->xMove = -mov; // collided = 1; //// cout << "C"; // } // } // if ((1.0 - push) < cData->xPush) cData->xPush = 1.0 - push; // } // if (bdy > 0.2 && !collided){ //z collision // if (!player->isSprinting && dy < -0.0 && GETBLOCK(vvox::getBottomBlockData(chunk, lockedChunk, c)).collide == 0) { //auto crouch // if (player->getCrouch() != 1.0){ // if (dz < 0 && GETBLOCK(vvox::getBackBlockData(chunk, lockedChunk, c)).collide == 0) { // pushedDown = 1; // } else if (dz > 0 && GETBLOCK(vvox::getFrontBlockData(chunk, lockedChunk, c)).collide == 0) { // pushedDown = 1; // } // } // if (pushedDown && !player->isClinging){ // // cout << "A "; // mov = 0.2 * glSpeedFactor * stepMod; // if (mov > bdy - 0.2) mov = bdy - 0.2; // if (mov > cData->headSquish) cData->headSquish = mov; // } // //if (player->velocity.y > 0.0f) player->velocity.y = 0.0; // } // if (!pushedDown || dy > -0.2){ // if (dz > 0 && GETBLOCK(vvox::getFrontBlockData(chunk, lockedChunk, c)).collide == 0) { // mov = bdz*push; // if (mov > ABS(cData->zMove)) cData->zMove = mov; // // cout << "B"; // } else if (GETBLOCK(vvox::getBackBlockData(chunk, lockedChunk, c)).collide == 0) { // mov = bdz*push; // if (mov > ABS(cData->zMove)) cData->zMove = -mov; // // cout << "B"; // } // } // if ((1.0 - push) < cData->zPush) cData->zPush = 1.0 - push; // } } ================================================ FILE: SoA/Collision.h ================================================ #pragma once #include "Vorb/types.h" class Player; class Chunk; const i32 PLAYER_COLLISION_STEPS = 20; //bool RaySphere(float xc, float yc, float zc, float xd, float yd, float zd, float xs, float ys, float zs, float r, float *dist=NULL, coordinate3lf *point=NULL); //bool RayPlane(float nx, float ny, float nz, float xs, float ys, float zs, float xd, float yd, float zd, coordinate3lf &p1, coordinate3lf &p2, coordinate3lf &p3, coordinate3lf &p4, float *dist=NULL, coordinate3lf *point=NULL); //bool SphereSphere(coordinate3lf &p1, coordinate3lf p2, float radius1, float radius2); //bool SpherePlane(coordinate3lf &sp, coordinate3lf &vn, coordinate3lf &p1, coordinate3lf &p2, coordinate3lf &p3, coordinate3lf &p4, float r); //float TriangleArea(coordinate3lf &p1, coordinate3lf &p2, coordinate3lf &p3); //bool AABBCollision(coordinate3lf &b1, coordinate3lf &b2, coordinate3lf &r1, coordinate3lf &r2, coordinate3lf &vn); void aabbChunkCollision(Player* player, f64v3* playerPos, Chunk** chunks, ui8 size); ================================================ FILE: SoA/CollisionComponentUpdater.cpp ================================================ #include "stdafx.h" #include "CollisionComponentUpdater.h" #include "GameSystem.h" void CollisionComponentUpdater::update(GameSystem* gameSystem VORB_UNUSED) { // for (auto& it : gameSystem->aabbCollidable) { //TODO(Ben): this // } } ================================================ FILE: SoA/CollisionComponentUpdater.h ================================================ /// /// CollisionComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates collision components /// #pragma once #ifndef CollisionComponentUpdater_h__ #define CollisionComponentUpdater_h__ class GameSystem; class CollisionComponentUpdater { public: /// Updates collision components /// @param gameSystem: Game ECS void update(GameSystem* gameSystem); }; #endif // CollisionComponentUpdater_h__ ================================================ FILE: SoA/ColorFilterRenderStage.cpp ================================================ #include "stdafx.h" #include "ColorFilterRenderStage.h" #include "ShaderLoader.h" namespace { const cString VERT_SRC = R"( in vec4 vPosition; void main() { gl_Position = vPosition; } )"; const cString FRAG_SRC = R"( uniform vec4 unColor; out vec4 pColor; void main() { pColor = unColor; } )"; } void ColorFilterRenderStage::hook(vg::FullQuadVBO* quad) { m_quad = quad; } void ColorFilterRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED /*= nullptr*/) { if (!m_program.isCreated()) { m_program = ShaderLoader::createProgram("ColorFilterShader", VERT_SRC, FRAG_SRC); } m_program.use(); glUniform4fv(m_program.getUniform("unColor"), 1, &m_color[0]); glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); m_program.unuse(); } ================================================ FILE: SoA/ColorFilterRenderStage.h ================================================ /// /// ColorFilterRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 25 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders a full screen color filter /// #pragma once #ifndef ColorFilterRenderStage_h__ #define ColorFilterRenderStage_h__ #include #include #include "IRenderStage.h" class ColorFilterRenderStage : public IRenderStage { public: void hook(vg::FullQuadVBO* quad); /// Draws the render stage virtual void render(const Camera* camera = nullptr) override; void setColor(const f32v4& color) { m_color = color; } private: f32v4 m_color; vg::FullQuadVBO* m_quad = nullptr; vg::GLProgram m_program; }; #endif // ColorFilterRenderStage_h__ ================================================ FILE: SoA/ColoredFullQuadRenderer.cpp ================================================ #include "stdafx.h" #include "ColoredFullQuadRenderer.h" #include const cString COL_VERT_SRC = R"( in vec4 vPosition; void main() { gl_Position = vPosition; } )"; const cString COL_FRAG_SRC = R"( uniform vec4 unColor; out vec4 fColor; void main() { fColor = unColor; } )"; ColoredFullQuadRenderer::~ColoredFullQuadRenderer() { if (m_program) { m_program->dispose(); delete m_program; } } void ColoredFullQuadRenderer::draw(vg::FullQuadVBO& quad, const f32v4& color) { // Lazy shader init if (!m_program) { m_program = new vg::GLProgram(true); m_program->addShader(vg::ShaderType::VERTEX_SHADER, COL_VERT_SRC); m_program->addShader(vg::ShaderType::FRAGMENT_SHADER, COL_FRAG_SRC); m_program->link(); m_program->initUniforms(); m_program->initAttributes(); } // Draw the quad m_program->use(); m_program->enableVertexAttribArrays(); glUniform4fv(m_program->getUniform("unColor"), 1, &color[0]); quad.draw(); m_program->disableVertexAttribArrays(); m_program->unuse(); } ================================================ FILE: SoA/ColoredFullQuadRenderer.h ================================================ /// /// ColoredFullQuadRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 22 Mar 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders a FullQuadVBO with color /// #pragma once #ifndef ColoredFullQuadRenderer_h__ #define ColoredFullQuadRenderer_h__ #include #include DECL_VG(class GLProgram); class ColoredFullQuadRenderer { public: ~ColoredFullQuadRenderer(); void draw(vg::FullQuadVBO& quad, const f32v4& color); private: vg::GLProgram* m_program = nullptr; }; #endif // ColoredFullQuadRenderer_h__ ================================================ FILE: SoA/CommonState.h ================================================ /// /// CommonState.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 4 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Container for states and controllers passed between screens. /// #pragma once #ifndef CommonState_h__ #define CommonState_h__ #include "LoadContext.h" #include #include #include "SpaceSystemRenderStage.h" #include "SkyboxRenderStage.h" #include "HdrRenderStage.h" struct SoaState; DECL_VSOUND(class Engine) DECL_VUI(class GameWindow) struct CommonState { public: SoaState* state = nullptr; vsound::Engine* soundEngine = nullptr; vui::GameWindow* window = nullptr; StaticLoadContext loadContext; struct { SkyboxRenderStage skybox; SpaceSystemRenderStage spaceSystem; HdrRenderStage hdr; } stages; // Shared render stages vg::FullQuadVBO quad; ///< Quad used for post-processing }; #endif // CommonState_h__ ================================================ FILE: SoA/Computer.cpp ================================================ #include "stdafx.h" #include "Computer.h" Computer::Computer() { // Empty } Computer::~Computer() { // Empty } ================================================ FILE: SoA/Computer.h ================================================ // // Computer.h // Seed Of Andromeda // // Created by Ben Arnold on 26 Oct 2014 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // This file provides the implementation for a virtual computer. // #pragma once #ifndef Computer_h_ #define Computer_h_ class Computer { public: Computer(); virtual ~Computer(); }; #endif // Computer_h_ ================================================ FILE: SoA/ConsoleFuncs.h ================================================ // // ConsoleFuncs.h // Seed of Andromeda // // Created by Cristian Zaloj on 30 Jun 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef ConsoleFuncs_h__ #define ConsoleFuncs_h__ #include #include #include "DLLAPI.h" #include "SoAState.h" #include "SoaController.h" #include "SoaEngine.h" #include "ConsoleTests.h" #include template void runScript(vscript::IEnvironment* env, const cString file) { env->run(nString(file)); } #ifdef VORB_OS_WINDOWS int loadDLL(const cString name) { HINSTANCE dll = LoadLibrary(name); int(*f)() = (int(*)())GetProcAddress(dll, "getCode"); fflush(stderr); fflush(stdout); return f(); } #endif//VORB_OS_WINDOWS template T* create() { T* v = new T(); return v; } template void free(T* v) { delete v; } void initState(SoaState* s, const cString spaceSystemDir) { SoaEngine::initState(s); SoaEngine::loadSpaceSystem(s, spaceSystemDir); } std::thread* startGame(SoaState* s, SoaController* c) { std::thread* t = new std::thread([=] () { c->startGame(s); while (true) { // Sleep(100); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return; }); return t; } void stopGame(std::thread* t) { bool isRunning = true; std::thread printer([&] () { puts(""); size_t i = 2; char buf[] = { '\r', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ' , '\0' }; while (isRunning) { puts(buf); i++; buf[i] = ' '; i %= 8; buf[i + 1] = '.'; // Sleep(250); std::this_thread::sleep_for(std::chrono::milliseconds(250)); } }); t->join(); isRunning = false; delete t; } void setStartingPlanet(SoaState* s, vecs::EntityID eID) { s->clientState.startingPlanet = eID; } template void registerFuncs(vscript::IEnvironment* env) { env->setNamespaces("SC"); /************************************************************************/ /* Application methods */ /************************************************************************/ env->addValue("env", env); env->addCDelegate("run", makeDelegate(runScript)); #ifdef VORB_OS_WINDOWS env->addCDelegate("loadDLL", makeDelegate(loadDLL)); #endif//VORB_OS_WINDOWS /************************************************************************/ /* Game-specific methods */ /************************************************************************/ env->addCDelegate("createState", makeDelegate(create)); env->addCDelegate("freeState", makeDelegate(free)); env->addCDelegate("createController", makeDelegate(create)); env->addCDelegate("freeController", makeDelegate(free)); env->addCDelegate("initState", makeDelegate(initState)); env->addCDelegate("startGame", makeDelegate(startGame)); env->addCDelegate("stopGame", makeDelegate(stopGame)); env->addCDelegate("setStartingPlanet", makeDelegate(setStartingPlanet)); /************************************************************************/ /* Test methods */ /************************************************************************/ env->setNamespaces("CAS"); env->addCDelegate("create", makeDelegate(createCASData)); env->addCDelegate("run", makeDelegate(runCAS)); env->addCDelegate("free", makeDelegate(freeCAS)); env->setNamespaces("CHS"); env->addCDelegate("run", makeDelegate(runCHS)); env->setNamespaces(); } #endif // ConsoleFuncs_h__ ================================================ FILE: SoA/ConsoleMain.h ================================================ // // ConsoleMain.h // Seed of Andromeda // // Created by Cristian Zaloj on 29 Jun 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Main entry point for the console version of the application. // #pragma once #ifndef ConsoleMain_h__ #define ConsoleMain_h__ #include #include #include "ConsoleFuncs.h" #ifdef VORB_OS_WINDOWS #define SOA_CONSOLE_COLOR_HEADER (FOREGROUND_BLUE | FOREGROUND_INTENSITY) #define SOA_CONSOLE_COLOR_PROMPT (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY) #define SOA_CONSOLE_COLOR_MAIN (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) #define SOA_CONSOLE_COLOR_OUTPUT (FOREGROUND_GREEN | FOREGROUND_INTENSITY) #define SOA_CONSOLE_COLOR_ERROR (FOREGROUND_RED | FOREGROUND_INTENSITY) #else//VORB_OS_WINDOWS #define SOA_CONSOLE_COLOR_HEADER 34 #define SOA_CONSOLE_COLOR_PROMPT 33 #define SOA_CONSOLE_COLOR_MAIN 37 #define SOA_CONSOLE_COLOR_OUTPUT 32 #define SOA_CONSOLE_COLOR_ERROR 31 #endif//VORB_OS_WINDOWS namespace { struct ConsolePrinter { public: void setColor(ui32 color) { if (m_lastColor != color) { fflush(stdout); fflush(stderr); } m_lastColor = color; #ifdef VORB_OS_WINDOWS SetConsoleTextAttribute(hndConsole, color); #else printf("\033[0;%dm", color); #endif//VORB_OS_WINDOWS } void out(Sender, const cString msg) { setColor(SOA_CONSOLE_COLOR_OUTPUT); puts(msg); } void err(Sender, const cString msg) { setColor(SOA_CONSOLE_COLOR_ERROR); puts(msg); } #ifdef VORB_OS_WINDOWS HANDLE hndConsole; #endif//VORB_OS_WINDOWS private: ui32 m_lastColor = 0; }; } template void consoleMain() { // Get console for manipulation ConsolePrinter printer = {}; #ifdef VORB_OS_WINDOWS printer.hndConsole = GetStdHandle(STD_OUTPUT_HANDLE); #endif//VORB_OS_WINDOWS // Write out that we are using the console version printer.setColor(SOA_CONSOLE_COLOR_HEADER); puts(R"( _________________ / / / / / SoA Console / / / ______ / / |v0.1| ========================== )"); vscript::IEnvironment* env = new ScriptImpl(); vscript::ConsoleBackend repl; repl.init(env); registerFuncs(env); repl.onConsoleOutput.out += makeDelegate(&printer, &ConsolePrinter::out); repl.onConsoleOutput.err += makeDelegate(&printer, &ConsolePrinter::err); char buf[1024]; while (true) { printer.setColor(SOA_CONSOLE_COLOR_PROMPT); printf(">>> "); printer.setColor(SOA_CONSOLE_COLOR_MAIN); std::cin.getline(buf, 1024); repl.invokeCommand(buf); } delete env; } #endif // ConsoleMain_h__ ================================================ FILE: SoA/ConsoleTests.cpp ================================================ #include "stdafx.h" #include "ConsoleTests.h" #include "ChunkAllocator.h" #include "ChunkAccessor.h" #include #include struct ChunkAccessSpeedData { size_t numThreads; std::thread* threads; std::mutex lock; std::condition_variable cv; PagedChunkAllocator allocator; ChunkAccessor accessor; ChunkID* ids; ChunkHandle* handles; }; ChunkAccessSpeedData* createCASData(size_t numThreads, size_t requestCount, ui64 maxID) { ChunkAccessSpeedData* data = new ChunkAccessSpeedData; data->accessor.init(&data->allocator); // Create the thread pools data->numThreads = numThreads; data->threads = new std::thread[numThreads] {}; for (size_t threadID = 0; threadID < numThreads; threadID++) { std::thread([data, threadID, requestCount] () { { // Wait until everyone is notified std::unique_lock lock(data->lock); printf("Thread %zu awaiting notification\n", threadID); data->cv.wait(lock); } std::mt19937 rEngine(threadID); std::uniform_int_distribution release(0, 1); // Begin requesting chunks printf("Thread %zu starting\n", threadID); PreciseTimer timer; timer.start(); ChunkID* id = data->ids + (requestCount * threadID); ChunkHandle* hndAcquire = data->handles + (requestCount * threadID); ChunkHandle* hndRelease = hndAcquire; ChunkHandle* hndEnd = hndRelease + requestCount; while (hndRelease != hndEnd) { if ((hndAcquire > hndRelease) && release(rEngine)) { // Release a handle hndRelease->release(); hndRelease++; } else if(hndAcquire != hndEnd) { // Acquire a handle *hndAcquire = data->accessor.acquire(*id); hndAcquire++; id++; } } printf("Thread %zu finished in %lf ms\n", threadID, timer.stop()); }).swap(data->threads[threadID]); data->threads[threadID].detach(); } // Create the random requests data->ids = new ChunkID[requestCount * numThreads]; data->handles = new ChunkHandle[requestCount * numThreads]{}; for (size_t i = 0; i < requestCount * numThreads; i++) { data->ids[i] = rand() % maxID; } return data; } void runCAS(ChunkAccessSpeedData* data) { // Start the races data->cv.notify_all(); } void freeCAS(ChunkAccessSpeedData* data) { printf("Chunks Alive: %zu\n", data->accessor.getCountAlive()); fflush(stdout); data->accessor.destroy(); delete[] data->ids; delete[] data->handles; delete[] data->threads; delete data; } void runCHS() { PagedChunkAllocator allocator = {}; ChunkAccessor accessor = {}; accessor.init(&allocator); ChunkHandle h1 = accessor.acquire(1); ChunkHandle h2 = h1; h2 = h2.acquire(); h2.release(); h1.release(); } ================================================ FILE: SoA/ConsoleTests.h ================================================ // // ConsoleTests.h // Seed of Andromeda // // Created by Cristian Zaloj on 1 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef ConsoleTests_h__ #define ConsoleTests_h__ #include "Chunk.h" /************************************************************************/ /* Chunk Access Speed */ /************************************************************************/ struct ChunkAccessSpeedData; ChunkAccessSpeedData* createCASData(size_t numThreads, size_t requestCount, ui64 maxID); void runCAS(ChunkAccessSpeedData* data); void freeCAS(ChunkAccessSpeedData* data); void runCHS(); #endif // !ConsoleTests_h__ ================================================ FILE: SoA/Constants.h ================================================ /// /// Constants.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 17 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Defines helpful SoA specific constants /// #pragma once #ifndef Constants_h__ #define Constants_h__ #define SOA_VERSION 0.1.5 #ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include #include #endif #include // Uncomment for advanced heap checking #ifdef _DEBUG //#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) //#define new DEBUG_NEW #endif const i32 CHUNK_WIDTH = 32; // Chunk Width Minus 1 const i32 CHUNK_WIDTH_M1 = 31; const i32 HALF_CHUNK_WIDTH = CHUNK_WIDTH / 2; const i32 CHUNK_LAYER = CHUNK_WIDTH*CHUNK_WIDTH; const i32 CHUNK_SIZE = CHUNK_LAYER*CHUNK_WIDTH; const i32 SURFACE_DEPTH = 256; const i32 OBJECT_LIST_SIZE = 24096; #define M_SOL 1989100000000000000000000000000.0 /*** Helpful conversion factors ***/ const f64 KM_PER_M = 0.001; const f64 M_PER_KM = 1000.0; const f64 KM_PER_VOXEL = 0.0005; const f64 M_PER_VOXEL = 0.5; const f64 VOXELS_PER_M = 2.0; const f64 VOXELS_PER_KM = 2000.0; // Useful sentinal value to use in place of DBL_MAX for distance checks and such, // since DBL_MAX is just too damn big and can overflow with any math const f64 DOUBLE_SENTINEL = 10000000000000000000000000000000000000000000.0; const f64 DEG_TO_RAD = M_PI / 180.0; #define BIT(i) (1 << (i)) #endif // Constants_h__ ================================================ FILE: SoA/CutoutVoxelRenderStage.cpp ================================================ #include "stdafx.h" #include "CutoutVoxelRenderStage.h" #include #include "Camera.h" #include "Chunk.h" #include "BlockPack.h" #include "BlockTexturePack.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "SoaOptions.h" #include "RenderUtils.h" #include "ShaderLoader.h" void CutoutVoxelRenderStage::hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams) { m_renderer = renderer; m_gameRenderParams = gameRenderParams; } void CutoutVoxelRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { ChunkMeshManager* cmm = m_gameRenderParams->chunkMeshmanager; const f64v3& position = m_gameRenderParams->chunkCamera->getPosition(); m_renderer->beginCutout(m_gameRenderParams->blockTexturePack->getAtlasTexture(), m_gameRenderParams->sunlightDirection, m_gameRenderParams->sunlightColor); glDisable(GL_CULL_FACE); // f64v3 cpos; // TODO: Implement the saving mechanism/throw it out. // static GLuint saveTicks = SDL_GetTicks(); // bool save = 0; // if (SDL_GetTicks() - saveTicks >= 60000) { //save once per minute // save = 1; // saveTicks = SDL_GetTicks(); // } ChunkMesh *cm; const std::vector & chunkMeshes = cmm->getChunkMeshes(); { std::lock_guard l(cmm->lckActiveChunkMeshes); if (chunkMeshes.empty()) return; for (int i = chunkMeshes.size() - 1; i >= 0; i--) { cm = chunkMeshes[i]; if (cm->inFrustum) { m_renderer->drawCutout(cm, position, m_gameRenderParams->chunkCamera->getViewProjectionMatrix()); } } } glEnable(GL_CULL_FACE); m_renderer->end(); } ================================================ FILE: SoA/CutoutVoxelRenderStage.h ================================================ /// /// CutoutVoxelRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the render stage for cutout voxels. /// cutout voxels have pixels that are either fully opaque, or /// fully transparent, and it's shader uses glDiscard to discard /// transparent fragments. /// #pragma once #ifndef CutoutVoxelRenderStage_h__ #define CutoutVoxelRenderStage_h__ #include "IRenderStage.h" #include class Camera; class ChunkRenderer; class GameRenderParams; class MeshManager; class CutoutVoxelRenderStage : public IRenderStage { public: void hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams); /// Draws the render stage virtual void render(const Camera* camera) override; private: ChunkRenderer* m_renderer; const GameRenderParams* m_gameRenderParams; ///< Handle to some shared parameters }; #endif // CutoutVoxelRenderStage_h__ ================================================ FILE: SoA/DLLAPI.h ================================================ // // DLLAPI.h // #pragma once #ifndef DLLAPI_h__ namespace DLLAPI { struct Information { const cString name; ///< The name of the DLL const cString friendlyName; ///< A human readable form of the DLL union { struct { i32 major : 8; i32 minor : 12; i32 revision : 12; }; i32 id; } version; ///< Versioning information }; typedef void (*FuncRetrieveInformation)(DLLAPI::Information* info); typedef void (*FuncFillFuntionTable)(void*** table, size_t* count); } #endif // DLLAPI_h__ ================================================ FILE: SoA/DLLLoader.h ================================================ // // DLLLoader.h // Seed of Andromeda // // Created by Cristian Zaloj on 4 Jul 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef DLLLoader_h__ #define DLLLoader_h__ #endif // DLLLoader_h__ ================================================ FILE: SoA/DebugRenderer.cpp ================================================ #include "stdafx.h" #include "DebugRenderer.h" #include #include #include #include #include #include "GameManager.h" #include "RenderUtils.h" #include "ShaderLoader.h" namespace { const cString VERT_SRC = R"( // Uniforms uniform mat4 unWVP; // Input in vec4 vPosition; // Position in object space in vec4 vColor; out vec4 fColor; void main() { fColor = vColor; gl_Position = unWVP * vPosition; } )"; const cString FRAG_SRC = R"( in vec4 fColor; // Output out vec4 pColor; void main() { pColor = fColor; } )"; } f32v3 findMidpoint(f32v3 vertex1, f32v3 vertex2); class Vec3KeyFuncs { public: size_t operator()(const f32v3& k)const { return std::hash()(k.x) ^ std::hash()(k.y) ^ std::hash()(k.z); } bool operator()(const f32v3& a, const f32v3& b)const { return a.x == b.x && a.y == b.y && a.z == b.z; } }; DebugRenderer::~DebugRenderer() { if (m_program.isCreated()) { m_program.dispose(); glDeleteBuffers(1, &m_vbo); glDeleteBuffers(1, &m_ibo); } } void DebugRenderer::render(const f32m4 &vp, const f64v3& playerPos, const f32m4& w /* = f32m4(1.0) */) { vg::RasterizerState::CULL_NONE.set(); m_previousTimePoint = m_currentTimePoint; m_currentTimePoint = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsedTime = m_currentTimePoint - m_previousTimePoint; double deltaT = elapsedTime.count(); if (!m_program.isCreated()) { m_program = ShaderLoader::createProgram("DebugRenderer", VERT_SRC, FRAG_SRC); glGenBuffers(1, &m_vbo); glGenBuffers(1, &m_ibo); } m_program.use(); m_program.enableVertexAttribArrays(); for (auto& c : m_contexts) { // if (c.second.icospheres.size()) renderIcospheres(c.second.icospheres, vp, w, playerPos, deltaT); // if (c.second.cubes.size()) renderCubes(c.second.cubes, vp, w, playerPos, deltaT); if (c.second.lines.size()) renderLines(c.second.lines, vp, w, playerPos, deltaT); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); m_program.disableVertexAttribArrays(); m_program.use(); } void DebugRenderer::drawIcosphere(const f64v3 &position, const float radius, const color4 &color, const int lod, ui32 context /*= 0*/, const double duration) { auto lookup = m_icosphereMeshes.find(lod); if(lookup == m_icosphereMeshes.end()) { createIcosphere(lod); } Icosphere sphere; sphere.color = color; sphere.lod = lod; sphere.position = position; sphere.radius = radius; sphere.timeTillDeletion = duration; m_contexts[context].icospheres.push_back(sphere); } void DebugRenderer::drawCube(const f64v3 &position, const f64v3 &size, const color4 &color, ui32 context /*= 0*/, const double duration) { Cube cube; cube.position = position; cube.size = size; cube.color = color; cube.timeTillDeletion = duration; m_contexts[context].cubes.push_back(cube); } void DebugRenderer::drawLine(const f64v3 &startPoint, const f64v3 &endPoint, const color4 &color, ui32 context /*= 0*/, const double duration) { Line line; line.color = color; line.position1 = startPoint; line.position2 = endPoint; line.timeTillDeletion = duration; m_contexts[context].lines.push_back(line); } void DebugRenderer::renderIcospheres(std::vector& icospheres VORB_UNUSED, const f32m4 &vp VORB_UNUSED, const f32m4& w VORB_UNUSED, const f64v3& playerPos VORB_UNUSED, const double deltaT VORB_UNUSED) { /* f32m4 modelMatrix(1.0f); for (auto i = icospheres.begin(); i != icospheres.end(); i++) { SimpleMesh* mesh = m_icosphereMeshes.at(i->lod); glBindBuffer(GL_ARRAY_BUFFER, mesh->vertexBufferID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->indexBufferID); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(f32v3), 0); setMatrixTranslation(modelMatrix, i->position, playerPos); setMatrixScale(modelMatrix, i->radius); f32m4 mvp = vp * modelMatrix * w; glUniform4f(m_program.getUniform("unColor"), i->color.r, i->color.g, i->color.b, i->color.a); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &mvp[0][0]); glDrawElements(GL_TRIANGLES, mesh->numIndices, GL_UNSIGNED_INT, 0); i->timeTillDeletion -= deltaT; } icospheres.erase(std::remove_if(icospheres.begin(), icospheres.end(), [](const Icosphere& sphere) { return sphere.timeTillDeletion <= 0; }), icospheres.end());*/ } void DebugRenderer::renderCubes(std::vector& cubes VORB_UNUSED, const f32m4 &vp VORB_UNUSED, const f32m4& w VORB_UNUSED, const f64v3& playerPos VORB_UNUSED, const double deltaT VORB_UNUSED) { /* glBindBuffer(GL_ARRAY_BUFFER, m_cubeMesh->vertexBufferID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_cubeMesh->indexBufferID); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(f32v3), 0); f32m4 modelMatrix(1.0f); for (auto i = cubes.begin(); i != cubes.end(); i++) { setMatrixTranslation(modelMatrix, i->position, playerPos); setMatrixScale(modelMatrix, i->size); f32m4 mvp = vp * modelMatrix * w; glUniform4f(m_program.getUniform("unColor"), i->color.r, i->color.g, i->color.b, i->color.a); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &mvp[0][0]); glDrawElements(GL_TRIANGLES, m_cubeMesh->numIndices, GL_UNSIGNED_INT, 0); i->timeTillDeletion -= deltaT; } cubes.erase(std::remove_if(_cubesToRender.begin(), _cubesToRender.end(), [](const Cube& cube) { return cube.timeTillDeletion <= 0; }), cubes.end());*/ } void DebugRenderer::renderLines(std::vector& lines, const f32m4 &vp, const f32m4& w, const f64v3& playerPos, const double deltaT) { std::vector m_vertices(lines.size() * 2); int index = 0; for (auto& l : lines) { m_vertices[index].position = f32v3(l.position1 - playerPos); m_vertices[index].color = l.color; m_vertices[index + 1].position = f32v3(l.position2 - playerPos); m_vertices[index + 1].color = l.color; index += 2; l.timeTillDeletion -= deltaT; } glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(SimpleMeshVertex), nullptr, GL_DYNAMIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, m_vertices.size() * sizeof(SimpleMeshVertex), m_vertices.data()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, GL_FALSE, sizeof(SimpleMeshVertex), offsetptr(SimpleMeshVertex, position)); glVertexAttribPointer(m_program.getAttribute("vColor"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SimpleMeshVertex), offsetptr(SimpleMeshVertex, color)); f32m4 mvp = vp * w; glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &mvp[0][0]); glDrawArrays(GL_LINES, 0, m_vertices.size()); lines.erase(std::remove_if(lines.begin(), lines.end(), [](const Line& line) { return line.timeTillDeletion <= 0; }), lines.end()); } void DebugRenderer::createIcosphere(const int lod) { std::vector indices; std::vector positions; vmesh::generateIcosphereMesh(lod, indices, positions); // m_icosphereMeshes[lod] = createMesh(positions.data(), positions.size(), indices.data(), indices.size()); } inline f32v3 findMidpoint(const f32v3& vertex1, const f32v3& vertex2) { return glm::normalize(f32v3((vertex1.x + vertex2.x) / 2.0f, (vertex1.y + vertex2.y) / 2.0f, (vertex1.z + vertex2.z) / 2.0f)); } ================================================ FILE: SoA/DebugRenderer.h ================================================ #pragma once #include #include #include const static float GOLDEN_RATIO = 1.61803398875f; const static int NUM_CUBE_VERTICES = 8; const static f32v3 CUBE_VERTICES[8] = { //front f32v3(-0.5f, -0.5f, 0.5f), f32v3(0.5f, -0.5f, 0.5f), f32v3(0.5f, 0.5f, 0.5f), f32v3(-0.5f, 0.5f, 0.5f), // back f32v3(-0.5f, -0.5f, -0.5f), f32v3(0.5f, -0.5f, -0.5f), f32v3(0.5f, 0.5f, -0.5f), f32v3(-0.5f, 0.5f, -0.5f), }; const static int NUM_CUBE_INDICES = 36; const static GLuint CUBE_INDICES[36] = { // front 0, 1, 2, 2, 3, 0, // top 3, 2, 6, 6, 7, 3, // back 7, 6, 5, 5, 4, 7, // bottom 4, 5, 1, 1, 0, 4, // left 4, 0, 3, 3, 7, 4, // right 1, 5, 6, 6, 2, 1, }; struct SimpleMeshVertex { f32v3 position; color4 color; }; struct SimpleMesh { std::vector vertices; std::vector indices; }; struct Icosphere { f64v3 position; float radius; color4 color; int lod; double timeTillDeletion; //ms }; struct Cube { f64v3 position; f64v3 size; color4 color; double timeTillDeletion; //ms }; struct Line { f64v3 position1; f64v3 position2; color4 color; double timeTillDeletion; //ms }; struct DebugRenderContext { std::vector icospheres; std::vector cubes; std::vector lines; }; class DebugRenderer { public: ~DebugRenderer(); void render(const f32m4 &vp, const f64v3& viewPosition, const f32m4& w = f32m4(1.0)); void drawIcosphere(const f64v3 &position, const float radius, const color4 &color, const int lod, ui32 context = 0, const double duration = FLT_MAX); void drawCube(const f64v3 &position, const f64v3 &size, const color4 &color, ui32 context = 0, const double duration = FLT_MAX); void drawLine(const f64v3 &startPoint, const f64v3 &endPoint, const color4 &color, ui32 context = 0, const double duration = FLT_MAX); private: void renderIcospheres(std::vector& icospheres, const f32m4& vp, const f32m4& w, const f64v3& viewPosition, const double deltaT); void renderCubes(std::vector& cubes, const f32m4& vp, const f32m4& w, const f64v3& viewPosition, const double deltaT); void renderLines(std::vector& lines, const f32m4& v, const f32m4& w, const f64v3& viewPosition, const double deltaT); void createIcosphere(const int lod); std::map m_contexts; //Icosphere meshes sorted by level of detail std::map m_icosphereMeshes; // Program that is currently in use vg::GLProgram m_program; VGBuffer m_vbo = 0; VGIndexBuffer m_ibo = 0; std::chrono::high_resolution_clock::time_point m_previousTimePoint; std::chrono::high_resolution_clock::time_point m_currentTimePoint; }; ================================================ FILE: SoA/Density.cpp ================================================ #include "stdafx.h" #include "Density.h" #include "VoxelMatrix.h" const VoxelMatrix* gMatrix; // ---------------------------------------------------------------------------- float Sphere(const f32v3& worldPosition, const f32v3& origin, float radius) { return glm::length(worldPosition - origin) - radius; } // ---------------------------------------------------------------------------- float Cuboid(const f32v3& worldPosition, const f32v3& origin, const f32v3& halfDimensions) { const f32v3& local_pos = worldPosition - origin; const f32v3& pos = local_pos; const f32v3& d = glm::abs(pos) - halfDimensions; const float m = glm::max(d.x, glm::max(d.y, d.z)); return glm::min(m, glm::length(glm::max(d, f32v3(0.f)))); } // ---------------------------------------------------------------------------- float FractalNoise( const int octaves, const float frequency, const float lacunarity, const float persistence, const f32v2& position) { const float SCALE = 1.f / 128.f; f32v2 p = position * SCALE; float noise = 0.f; float amplitude = 1.f; p *= frequency; for (int i = 0; i < octaves; i++) { // noise += simplex(p) * amplitude; p *= lacunarity; amplitude *= persistence; } // move into [0, 1] range return 0.5f + (0.5f * noise); } // ---------------------------------------------------------------------------- float Density_Func(const f32v3& worldPosition) { i32v3 pos(glm::round(worldPosition)); float rv = 0.0f; if (gMatrix->getColorAndCheckBounds(pos + i32v3(gMatrix->size.x / 2, gMatrix->size.y / 2, gMatrix->size.z / 2)).a != 0) { rv += 100.0f; } else { rv -= 100.0f; } // return 20.0f; // const float MAX_HEIGHT = 20.f; // const float noise = FractalNoise(4, 0.5343f, 2.2324f, 0.68324f, vec2(worldPosition.x, worldPosition.z)); // const float terrain = worldPosition.y - (MAX_HEIGHT * noise); // const float cube = Cuboid(worldPosition, vec3(-4., 10.f, -4.f), vec3(12.f)); const float sphere = Sphere(worldPosition, f32v3(15.f, 2.5f, 1.f), 16.f); return sphere + rv; } ================================================ FILE: SoA/Density.h ================================================ #ifndef HAS_DENSITY_H_BEEN_INCLUDED #define HAS_DENSITY_H_BEEN_INCLUDED #include "Vorb/types.h" class VoxelMatrix; extern const VoxelMatrix* gMatrix; float Density_Func(const f32v3& worldPosition); #endif // HAS_DENSITY_H_BEEN_INCLUDED ================================================ FILE: SoA/DevConsole.cpp ================================================ #include "stdafx.h" #include "DevConsole.h" DevConsole DevConsole::m_instance; void DevConsole::init(int maxHistory) { // TODO(Ben): Add setCapacity new (&m_history) CommandRing(maxHistory); } void DevConsole::addListener(const nString& command, FuncNewCommand f, void* meta) { EventBinding eb = { f, meta }; m_commandListeners[command].emplace_back(eb); } // Adds listener for any command void DevConsole::addListener(FuncNewCommand f, void* meta) { EventBinding eb = { f, meta }; m_anyCommandListeners.emplace_back(eb); } bool DevConsole::removeListener(const nString& command, FuncNewCommand f) { auto it = m_commandListeners.find(command); if (it == m_commandListeners.end()) return false; auto& listeners = it->second; auto foundListener = std::find(listeners.begin(), listeners.end(), f); if (foundListener != listeners.end()) { listeners.erase(foundListener, foundListener + 1); return true; } return false; } bool DevConsole::removeListener(FuncNewCommand f) { auto foundListener = std::find(m_anyCommandListeners.begin(), m_anyCommandListeners.end(), f); if (foundListener != m_anyCommandListeners.end()) { m_anyCommandListeners.erase(foundListener, foundListener + 1); return true; } return false; } void DevConsole::addCommand(const nString& s) { m_commandListeners.emplace(s, std::vector()); } bool DevConsole::write(nString s) { // Remove leading ` while (s.size() && s.front() == '`') { s.erase(0, 1); } if (s.empty()) return false; // TODO(Ben): Concern about thread safety. // Ringbuffer push invalidates data... have to copy nString sCopy = s; m_history.push(sCopy); // Broadcast to listeners listening for any event for (auto& eb : m_anyCommandListeners) { eb.function(eb.metaData, s); } // Broadcast to specific listeners. nString command = getFirstToken(s); auto it = m_commandListeners.find(command); if (it == m_commandListeners.end()) return false; for (auto& eb : it->second) { eb.function(eb.metaData, s); } return true; } void DevConsole::toggleFocus() { m_isFocused = !m_isFocused; if (m_isFocused) { // Enable input vui::InputDispatcher::key.onKeyDown += makeDelegate(this, &DevConsole::onKeyDown); vui::InputDispatcher::key.onText += makeDelegate(this, &DevConsole::onTextInput); } else { // Disable input vui::InputDispatcher::key.onKeyDown -= makeDelegate(this, &DevConsole::onKeyDown); vui::InputDispatcher::key.onText -= makeDelegate(this, &DevConsole::onTextInput); } } void DevConsole::setFocus(bool focus) { if (focus != m_isFocused) { toggleFocus(); } } const nString& DevConsole::getHistory(const i32& index) { return m_history.at(index); } nString DevConsole::getFirstToken(nString input) { size_t i = 0; while (i < input.size()) { while (input[i] == ' ') i++; size_t start = i; while (input[i] != ' ' && i < input.size()) i++; if (i - start > 0) { return input.substr(start, i - start); } } return ""; } void DevConsole::tokenize(nString& input, OUT std::vector& tokens) { // TODO(Ben): Pass in delimiters size_t i = 0; while (i < input.size()) { while (input[i] == ' ') i++; size_t start = i; while (input[i] != ' ' && i < input.size()) i++; if (i - start > 0) { tokens.emplace_back(input.substr(i, i - start)); } } } void DevConsole::onKeyDown(Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& ev) { // TODO(Ben): Unicode is gonna be a nightmare if (ev.keyCode == VKEY_RETURN || ev.keyCode == VKEY_RETURN2) { write(m_currentLine); m_currentLine = ""; } else if (ev.keyCode == VKEY_BACKSPACE || ev.keyCode == VKEY_DELETE) { if (m_currentLine.size()) m_currentLine.pop_back(); } } void DevConsole::onTextInput(Sender s VORB_MAYBE_UNUSED, const vui::TextEvent& ev) { const char* t = ev.text; while (*t != '\0') { m_currentLine += (*t); t++; } } ================================================ FILE: SoA/DevConsole.h ================================================ #pragma once #include #include typedef vorb::ring_buffer CommandRing; typedef void(*FuncNewCommand)(void* metadata, const nString& command); class DevConsole { public: DevConsole() : m_history(50) {}; static DevConsole& getInstance() { return m_instance; } void init(int maxHistory); // Adds listener for a specific command void addListener(const nString& command, FuncNewCommand f, void* meta); // Adds listener for any command void addListener(FuncNewCommand f, void* meta); // Removes listener for specific command bool removeListener(const nString& command, FuncNewCommand f); // Removes listener for any command bool removeListener(FuncNewCommand f); void addCommand(const nString& s); bool write(nString s); void toggleFocus(); void setFocus(bool focus); const bool& isFocused() { return m_isFocused; } const nString& getHistory(const i32& index); const nString& getCurrentLine() { return m_currentLine; } // Utilities for tokenizing strings static nString getFirstToken(nString input); static void tokenize(nString& input, OUT std::vector& tokens); private: class EventBinding { public: FuncNewCommand function; void* metaData; bool operator== (const FuncNewCommand& f) { return function == f; } }; void onKeyDown(Sender s, const vui::KeyEvent& ev); void onTextInput(Sender s, const vui::TextEvent& ev); bool m_isFocused = false; nString m_currentLine = ""; CommandRing m_history; std::unordered_map> m_commandListeners; ///< For specific commands only std::vector m_anyCommandListeners; static DevConsole m_instance; ///< Singleton }; ================================================ FILE: SoA/DevConsoleView.cpp ================================================ #include "stdafx.h" #include "DevConsoleView.h" #include "textureUtils.h" #include #include #include #include #include #include "DevConsole.h" DevConsoleView::DevConsoleView() : m_renderRing(START_LINES_TO_RENDER) { } DevConsoleView::~DevConsoleView() { dispose(); } void DevConsoleView::init(DevConsole* console, i32 linesToRender, const f32v2& position, float lineWidth) { m_batch = new vg::SpriteBatch(); m_batch->init(); m_font = new vg::SpriteFont(); m_font->init("Fonts/orbitron_black-webfont.ttf", 32); m_position = position; m_dimensions.x = lineWidth; m_dimensions.y = linesToRender * m_font->getFontHeight() + 40.0f; m_linesToRender = linesToRender; m_renderRing.resize(linesToRender); m_renderRing.clear(); m_console = console; _fHook = [](void* meta, const nString& str) { ((DevConsoleView*)meta)->onNewCommand(str); }; m_console->addListener(_fHook, this); m_isViewModified = true; initSinglePixelTexture(m_texture, color::White); } void DevConsoleView::dispose() { if (m_batch) { m_batch->dispose(); m_batch = nullptr; } if (m_font) { m_font->dispose(); m_font = nullptr; } if (m_console) { if (_fHook) { m_console->removeListener(_fHook); _fHook = nullptr; } m_console = nullptr; } } void DevConsoleView::update(const f32& dt) { // Blinking Logic m_blinkTimeRemaining -= dt; if (m_blinkTimeRemaining < 0) { m_blinkTimeRemaining = DEV_CONSOLE_MARKER_BLINK_DELAY; m_isViewModified = true; } if (m_currentLine != m_console->getCurrentLine()) { m_currentLine = m_console->getCurrentLine(); m_isViewModified = true; } if (m_isViewModified) redrawBatch(); } void DevConsoleView::render(const f32v2& screenSize) { // Check For A Batch if (!m_batch) return; redrawBatch(); // TODO(Ben): Not every frame m_batch->render(screenSize, &vg::SamplerState::POINT_WRAP, &vg::DepthState::NONE, &vg::RasterizerState::CULL_NONE); } void DevConsoleView::setBackColor(const color4& color) { m_backColor = color; m_isViewModified = true; } void DevConsoleView::setFontColor(const color4& color) { m_fontColor = color; m_isViewModified = true; } void DevConsoleView::setPosition(const f32v2& pos) { m_position = pos; m_isViewModified = true; } void DevConsoleView::setDimensions(const f32v2& dims) { m_dimensions = dims; m_isViewModified = true; } void DevConsoleView::onNewCommand(const nString& str) { std::stringstream ss(str); std::string item; while (std::getline(ss, item, '\n')) { if (!m_renderRing.push(item)) { m_renderRing.pop(); m_renderRing.push(item); } } m_isViewModified = true; } void DevConsoleView::redrawBatch() { if (!m_batch || !m_font) return; m_batch->begin(); f32 textHeight = (f32)m_font->getFontHeight(); f32 yOffset = m_renderRing.size() * textHeight; // Draw dark transparent back m_batch->draw(m_texture, m_position - f32v2(10.0f, m_dimensions.y - textHeight - 10.0f), m_dimensions, color4(0, 0, 0, 176), 1.0f); // Draw Command Lines size_t i; for (i = 0; i < m_renderRing.size(); i++) { const cString cStr = m_renderRing.at(i).c_str(); if (cStr) { m_batch->drawString(m_font, cStr, m_position + f32v2(0.0f, textHeight * i + 10.0f - yOffset), f32v2(1), color4(0, 255, 0, 255), vg::TextAlign::TOP_LEFT, 0.9f); } } // Draw current line if (m_currentLine.size()) { m_batch->drawString(m_font, m_currentLine.c_str(), m_position + f32v2(0.0f, textHeight * i + 10.0f - yOffset), f32v2(1), color4(0, 255, 0, 255), vg::TextAlign::TOP_LEFT, 0.9f); } // TODO: Draw Input m_batch->end(vg::SpriteSortMode::BACK_TO_FRONT); } ================================================ FILE: SoA/DevConsoleView.h ================================================ #pragma once #include #include #include #include #include class DevConsole; DECL_VG(class SpriteBatch; class SpriteFont) typedef vorb::ring_buffer StringRing; const f32 DEV_CONSOLE_MARKER_BLINK_DELAY = 0.85f; const int START_LINES_TO_RENDER = 2; class DevConsoleView { public: DevConsoleView(); ~DevConsoleView(); // Position is of bottom left corner void init(DevConsole* console, i32 linesToRender, const f32v2& position, float lineWidth); void dispose(); void update(const f32& dt); void render(const f32v2& screenSize); /************************************************************************/ /* Setters */ /************************************************************************/ void setBackColor(const color4& color); void setFontColor(const color4& color); void setPosition(const f32v2& pos); void setDimensions(const f32v2& dims); /************************************************************************/ /* Getters */ /************************************************************************/ const color4& getBackColor() const { return m_backColor; } const color4& getFontColor() const { return m_fontColor; } const f32v2& getPosition() const { return m_position; } const f32v2& getDimensions() const { return m_dimensions; } private: void onNewCommand(const nString& str); void redrawBatch(); DevConsole* m_console = nullptr; void(*_fHook) (void*, const nString&); vg::SpriteBatch* m_batch = nullptr; vg::SpriteFont* m_font = nullptr; bool m_isViewModified = false; nString m_currentLine = ""; f32v2 m_position = f32v2(0.0f); f32v2 m_dimensions = f32v2(0.0f); color4 m_backColor = color4(0, 0, 0, 128); color4 m_fontColor = color::LightGreen; VGTexture m_texture; i32 m_linesToRender = START_LINES_TO_RENDER; StringRing m_renderRing; f32 m_blinkTimeRemaining = DEV_CONSOLE_MARKER_BLINK_DELAY; }; ================================================ FILE: SoA/DevHudRenderStage.cpp ================================================ #include "stdafx.h" #include "DevHudRenderStage.h" #include #include #include #include "App.h" DevHudRenderStage::DevHudRenderStage() { // Empty } DevHudRenderStage::~DevHudRenderStage() { delete _spriteBatch; delete _spriteFont; } void DevHudRenderStage::hook(const cString fontPath, i32 fontSize, const App* app, const f32v2& windowDims) { _spriteBatch = new vg::SpriteBatch(true, true); _spriteFont = new vg::SpriteFont(); _app = app; _windowDims = windowDims; _spriteFont->init(fontPath, fontSize); _fontHeight = _spriteFont->getFontHeight(); } void DevHudRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { // Reset the yOffset _yOffset = 0; _spriteBatch->begin(); // Draw crosshair if (_mode >= DevUiModes::CROSSHAIR) { drawCrosshair(); } // Fps Counters if (_mode >= DevUiModes::FPS) { drawFps(); } // Items in hands if (_mode >= DevUiModes::HANDS) { drawHands(); } // Positional text if (_mode >= DevUiModes::POSITION) { drawPosition(); } _spriteBatch->end(); // Render to the screen _spriteBatch->render(_windowDims); } void DevHudRenderStage::cycleMode(int offset /*= 1*/) { // The last element in DevUiModes const int last = static_cast(DevUiModes::LAST); // Treat it as an int when cycling so we can do integer math int imode = static_cast(_mode) + offset; if (imode < 0) { // Negative wrap imode = last - ((abs(imode) - 1) % (last + 1)); } else { // Positive wrap imode = imode % (last + 1); } // Cast it back to a DevUiMode _mode = static_cast(imode); } void DevHudRenderStage::drawCrosshair() { // const f32v2 cSize(26.0f); // _spriteBatch->draw(crosshairTexture.id, // (_windowDims - cSize) / 2.0f, // cSize, // ColorRGBA8(255, 255, 255, 128)); } void DevHudRenderStage::drawHands() { // const f32v2 SCALE(0.75f); // char buffer[256]; // Left Hand //if (_player->leftEquippedItem) { // std::sprintf(buffer, "Left Hand: %s (%d)", // _player->leftEquippedItem->name.c_str(), // _player->leftEquippedItem->count); // _spriteBatch->drawString(_spriteFont, // buffer, // f32v2(0.0f, _windowDims.y - _fontHeight), // SCALE, // color::White); //} //// Right Hand //if (_player->rightEquippedItem) { // std::sprintf(buffer, "Right Hand: %s (%d)", // _player->rightEquippedItem->name.c_str(), // _player->rightEquippedItem->count); // _spriteBatch->drawString(_spriteFont, // buffer, // f32v2(_windowDims.x - _spriteFont->measure(buffer).x, _windowDims.y - _fontHeight), // SCALE, // color::White); //} } void DevHudRenderStage::drawFps() { char buffer[256]; std::sprintf(buffer, "Render FPS: %.0f", _app->getFps()); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), f32v2(1.0f), color::White); _yOffset += _fontHeight; /* std::sprintf(buffer, "Physics FPS: %.0f", physicsFps); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), f32v2(1.0f), color::White); _yOffset += _fontHeight;*/ } void DevHudRenderStage::drawPosition() { // const f32v2 NUMBER_SCALE(0.75f); // char buffer[256]; // Grid position _yOffset += _fontHeight; _spriteBatch->drawString(_spriteFont, "Grid Position", f32v2(0.0f, _yOffset), f32v2(1.0f), color::White); _yOffset += _fontHeight; /* std::sprintf(buffer, "X %.2f", _player->headPosition.x); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight; std::sprintf(buffer, "Y %.2f", _player->headPosition.y); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight; std::sprintf(buffer, "Z %.2f", _player->headPosition.z); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight;*/ // World position _yOffset += _fontHeight; _spriteBatch->drawString(_spriteFont, "World Position", f32v2(0.0f, _yOffset), f32v2(1.0f), color::White); _yOffset += _fontHeight; /* std::sprintf(buffer, "X %-9.2f", _player->worldPosition.x); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight; std::sprintf(buffer, "Y %-9.2f", _player->worldPosition.y); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight; std::sprintf(buffer, "Z %-9.2f", _player->worldPosition.z); _spriteBatch->drawString(_spriteFont, buffer, f32v2(0.0f, _yOffset), NUMBER_SCALE, color::White); _yOffset += _fontHeight;*/ } ================================================ FILE: SoA/DevHudRenderStage.h ================================================ /// /// DevHudRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 2 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides the render stage for /// drawing the dev/debug Hud. /// #pragma once #ifndef DevHudRenderStage_h__ #define DevHudRenderStage_h__ #include "IRenderStage.h" #include DECL_VG(class SpriteBatch; class SpriteFont) class App; class DevHudRenderStage : public IRenderStage{ public: DevHudRenderStage(); ~DevHudRenderStage(); void hook(const cString fontPath, i32 fontSize, const App* app, const f32v2& windowDims); /// Draws the render stage virtual void render(const Camera* camera) override; /// Cycles the Hud mode /// @param offset: How much to offset the current mode void cycleMode(int offset = 1); // Each mode includes the previous mode enum class DevUiModes { NONE = 0, CROSSHAIR = 1, HANDS = 2, FPS = 3, POSITION = 4, LAST = POSITION // Make sure LAST is always last }; private: void drawCrosshair(); void drawHands(); void drawFps(); void drawPosition(); vg::SpriteBatch* _spriteBatch = nullptr; ///< For rendering 2D sprites vg::SpriteFont* _spriteFont = nullptr; ///< Font used by spritebatch DevUiModes _mode = DevUiModes::HANDS; ///< The mode for rendering f32v2 _windowDims; ///< Dimensions of the window const App* _app = nullptr; ///< Handle to the app int _fontHeight; ///< Height of the spriteFont int _yOffset; ///< Y offset accumulator }; #endif // DevHudRenderStage_h__ ================================================ FILE: SoA/DevScreen.cpp ================================================ #include "stdafx.h" #include "DevScreen.h" #include #include #include #include #include #define DEV_SCREEN_FONT "Fonts/orbitron_bold-webfont.ttf" #define DEV_SCREEN_FONT_SIZE 32 const cString TITLE = "Dev Screen"; const color4& FONT_COLOR = color::AliceBlue; i32 DevScreen::getNextScreen() const { if (m_nextScreen) return m_nextScreen->getIndex(); return SCREEN_INDEX_NO_SCREEN; } i32 DevScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void DevScreen::build() { // Empty } void DevScreen::destroy(const vui::GameTime&) { // Empty } void DevScreen::onEntry(const vui::GameTime&) { m_delegatePool.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&] (Sender, const vui::KeyEvent& e) { auto kvp = m_screenMapping.find((VirtualKey)e.keyCode); if (kvp == m_screenMapping.end()) return; m_nextScreen = kvp->second; }); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearDepth(1.0); m_nextScreen = nullptr; deferCounter = 1; m_sb = new vg::SpriteBatch(true, true); m_font = new vg::SpriteFont(); m_font->init(DEV_SCREEN_FONT, DEV_SCREEN_FONT_SIZE); } void DevScreen::onExit(const vui::GameTime&) { m_delegatePool.dispose(); m_sb->dispose(); delete m_sb; m_sb = nullptr; m_font->dispose(); delete m_font; m_font = nullptr; } void DevScreen::update(const vui::GameTime&) { if (m_nextScreen) { if (deferCounter) { --deferCounter; } else { m_state = vui::ScreenState::CHANGE_NEXT; } } } void DevScreen::draw(const vui::GameTime&) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); const vui::GameWindow* w = &m_game->getWindow(); m_sb->begin(); f32v2 pos(90.0f, 300.0f); f32 posInc = 35.0f; if (m_nextScreen) { // Draw loading message m_sb->drawString(m_font, "Loading... please wait.", f32v2(w->getWidth() * 0.5f, w->getHeight() * 0.5f), f32v2(1.0f), FONT_COLOR, vg::TextAlign::CENTER); } else { // Draw title m_sb->drawString(m_font, TITLE, f32v2((w->getWidth() - m_font->measure(TITLE).x * 1.5) / 2.0, 50.0f), f32v2(1.5f), FONT_COLOR); // Draw strings m_sb->drawString(m_font, "* Press one of the following keys to enter a screen:", pos, f32v2(1.0f), FONT_COLOR); pos.y += posInc * 2.0f; for (auto& it : m_screenMapping) { m_sb->drawString(m_font, m_screenNames[it.first].c_str(), pos, f32v2(1.0f), FONT_COLOR); pos.y += posInc; } } m_sb->end(); m_sb->render(f32v2(w->getWidth(), w->getHeight())); } void DevScreen::addScreen(VirtualKey vKey, vui::IGameScreen* s, const nString& name) { m_screenMapping[vKey] = s; m_screenNames[vKey] = nString(VirtualKeyStrings[vKey]) + ": " + name; } ================================================ FILE: SoA/DevScreen.h ================================================ /// /// DevScreen.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 14 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Allows developers to bypass normal startup into other screens /// #pragma once #ifndef DevScreen_h__ #define DevScreen_h__ #include #include #include #include DECL_VG(class SpriteBatch; class SpriteFont) class DevScreen : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; /// Registers a screen that developers can access /// @param vKey: Key code used to access the screen /// @param s: Screen /// @param name: Display name void addScreen(VirtualKey vKey, vui::IGameScreen* s, const nString& name); private: std::map m_screenMapping; ///< Stores available screen targets std::map m_screenNames; ///< Stores display names of screens AutoDelegatePool m_delegatePool; ///< Input hooks reservoir vui::IGameScreen* m_nextScreen = nullptr; ///< The next screen int deferCounter = 1; //< When this hits 0 we change screen vg::SpriteBatch* m_sb = nullptr; vg::SpriteFont* m_font = nullptr; }; #endif // DevScreen_h__ ================================================ FILE: SoA/Doxyfile.in ================================================ PROJECT_NAME = "@CMAKE_PROJECT_NAME@" PROJECT_NUMBER = @VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@ STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \ @PROJECT_BINARY_DIR@ INPUT = @doxy_main_page@ \ @PROJECT_SOURCE_DIR@ \ @PROJECT_BINARY_DIR@ FILE_PATTERNS = *.h \ *.cc RECURSIVE = YES USE_MDFILE_AS_MAINPAGE = @doxy_main_page@ ================================================ FILE: SoA/DualContouringMesher.cpp ================================================ #include "stdafx.h" #include "DualContouringMesher.h" #include "VoxelMatrix.h" #include "Density.h" #include #include "Octree.h" void DualContouringMesher::genMatrixMesh(const VoxelMatrix& matrix, std::vector& vertices, std::vector& indices) { int octreeSize = glm::max(glm::max(matrix.size.x, matrix.size.y), matrix.size.z); gMatrix = &matrix; octreeSize = 128; std::cout << octreeSize << std::endl; const int MAX_THRESHOLDS = 5; const float THRESHOLDS[MAX_THRESHOLDS] = { -1.f, 0.1f, 1.f, 10.f, 50.f }; int thresholdIndex = 3; PreciseTimer timer; OctreeNode* root = BuildOctree(i32v3(-octreeSize / 2), octreeSize, THRESHOLDS[thresholdIndex]); std::cout << timer.stop() << std::endl; timer.start(); GenerateMeshFromOctree(root, vertices, indices); std::cout << timer.stop() << std::endl; } ================================================ FILE: SoA/DualContouringMesher.h ================================================ /// /// DualContouringMesher.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 15 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// /// #pragma once #ifndef DualContouringMesher_h__ #define DualContouringMesher_h__ #include "Octree.h" #include "VoxelModelMesh.h" class VoxelMatrix; // TODO(Ben): I don't understand this // Source: http://ngildea.blogspot.com/2014/11/implementing-dual-contouring.html class DualContouringMesher { public: static void genMatrixMesh(const VoxelMatrix& matrix, std::vector& vertices, std::vector& indices); private: }; #endif // DualContouringMesher_h__ ================================================ FILE: SoA/ECS Specs.txt ================================================ Usage: - Systems Are All Loaded Into Main Table - Systems Are Initialized In Three Passes * Calls init(i32 pass) - Systems Are Updated Every Frame * Calls update(i32 frame, const GameTime& time) - Systems May Be Ordered To Load/Save Internal State * Calls i32 getInternalStateSize(), save(ubyte* output), load(ubyte* data) X==========X |Components| X==========X Position - f64v3 Absolute Position - f64v3 Position In Local Voxel World Physics - f32 Mass - f32v3 Acceleration - f32v3 Velocity - std::vector Impulse List Collision - "Collision Skin" X=======X |Systems| X=======X ? Gravity ? - Requires "Physics" Component - Adds Gravitational Force To Acceleration Physics - Requires "Position" Component - Applies Impulses To Positions - Simulates Positions As If They Were Point Bodies - Clears Acceleration Collision - Requires "Position" Component - Takes Collision Skins And Collides Them Against Each Other And The Terrain, Modifying The Position ================================================ FILE: SoA/ECSTemplates.cpp ================================================ #include "stdafx.h" #include "ECSTemplates.h" #include #include vecs::EntityID ECSTemplate::create(vecs::ECS& ecs) { // Create entity. auto e = ecs.addEntity(); // Add all components. for(auto& kvp : m_components) kvp.second->m_cID = ecs.addComponent(kvp.first, e); // Build all components. for(auto& kvp : m_components) kvp.second->build(ecs, e); // Run post-build for dependencies and such. for (auto& kvp : m_components) kvp.second->postBuild(ecs, e); return e; } void ECSTemplateLibrary::loadTemplate(const vpath& file) { ECSTemplate* t = new ECSTemplate(); keg::ReadContext context; context.env = keg::getGlobalEnvironment(); { // Parse YAML file vio::IOManager iom; const cString s = iom.readFileToString(file); context.reader.init(s); delete[] s; } { vfile f; file.asFile(&f); nString fileName = file.getLeaf(); nString templateName = fileName.substr(0, fileName.length() - 4); m_templates[templateName] = t; } auto node = context.reader.getFirst(); auto f = makeFunctor([&](Sender s VORB_MAYBE_UNUSED, const nString& component, keg::Node node) { auto bb = m_builders.find(component); if(bb != m_builders.end()) { ECSComponentBuilder* builder = bb->second(); builder->load(context, node); t->m_components[component] = builder; } context.reader.free(node); }); context.reader.forAllInMap(node, &f); context.reader.dispose(); } vecs::EntityID ECSTemplateLibrary::build(vecs::ECS& ecs, const nString& name) const { auto tmpl = m_templates.find(name); if(tmpl == m_templates.end()) return ID_GENERATOR_NULL_ID; return tmpl->second->create(ecs); } ECSTemplateLibrary::~ECSTemplateLibrary() { for(auto& kvp : m_templates) { delete kvp.second; } } ================================================ FILE: SoA/ECSTemplates.h ================================================ // // ECSTemplates.h // // Created by Cristian Zaloj on 16 Mar 2015 // #pragma once #ifndef ECSTemplates_h__ #define ECSTemplates_h__ #include #include #include #include class ECSTemplate; class ECSTemplateLibrary; class ECSComponentBuilder { friend class ECSTemplate; public: virtual ~ECSComponentBuilder() { // Empty } virtual void load(keg::ReadContext& reader, keg::Node node) = 0; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) = 0; virtual void postBuild(vecs::ECS& ecs VORB_UNUSED, vecs::EntityID eID VORB_UNUSED) { /* Empty */ }; protected: vecs::ComponentID m_cID; ///< ID of generated component }; class ECSTemplate { friend class ECSTemplateLibrary; public: virtual ~ECSTemplate() { for(auto& kvp : m_components) delete kvp.second; } vecs::EntityID create(vecs::ECS& ecs); private: std::unordered_map m_components; }; class ECSTemplateLibrary { typedef Delegate ComponentBuildFunctionFactory; public: virtual ~ECSTemplateLibrary(); vecs::EntityID build(vecs::ECS& ecs, const nString& name) const; void loadTemplate(const vpath& file); template void registerFactory(const nString& component) { auto func=[]() -> ECSComponentBuilder* { return new T(); }; m_builders[component] = makeDelegate(&func); } template void forEachTemplate(F f) { for(auto& kvp : m_templates) f(kvp.first); } private: std::unordered_map m_templates; std::unordered_map m_builders; }; #endif // !ECSTemplates_h__ ================================================ FILE: SoA/Errors.cpp ================================================ #include "stdafx.h" #include "Errors.h" #include #ifndef VORB_OS_WINDOWS #include #include #endif//VORB_OS_WINDOWS void showMessage(const nString& message VORB_MAYBE_UNUSED) { #if defined(_WIN32) || defined(_WIN64) //SDL_WM_IconifyWindow(); SDL_Delay(100); SDL_SetRelativeMouseMode(SDL_FALSE); MessageBox(NULL, message.c_str(), "SoA", MB_OK); #else std::cout << "ERROR! MESSAGE BOX NOT IMPLEMENTED FOR THIS FILE SYSTEM\n"; int a; std::cin >> a; #endif } //yes 1, no 0 int showYesNoBox(const nString& message VORB_MAYBE_UNUSED) { #if defined(_WIN32) || defined(_WIN64) SDL_Delay(100); SDL_SetRelativeMouseMode(SDL_FALSE); int id = MessageBox(NULL, message.c_str(), "SoA", MB_YESNO); if (id == IDYES) return 1; if (id == IDNO) return 0; return 0; #else std::cout << "ERROR! YESNO BOX NOT IMPLEMENTED FOR THIS FILE SYSTEM\n"; int a; std::cin >> a; return 0; #endif } ///yes 1, no 0, cancel -1 int showYesNoCancelBox(const nString& message VORB_MAYBE_UNUSED) { #if defined(_WIN32) || defined(_WIN64) SDL_Delay(100); SDL_SetRelativeMouseMode(SDL_FALSE); int id = MessageBox(NULL, message.c_str(), "SoA", MB_YESNOCANCEL); if (id == IDYES) return 1; if (id == IDNO) return 0; if (id == IDCANCEL) return -1; return 0; #else std::cout << "ERROR! YESNO BOX NOT IMPLEMENTED FOR THIS FILE SYSTEM\n"; int a; std::cin >> a; return 0; #endif } nString getFullPath(const char *initialDir) { nString rval; #ifdef VORB_OS_WINDOWS char pathBuffer[1024]; _fullpath(pathBuffer, initialDir, 1024); #else//VORB_OS_WINDOWS char pathBuffer[PATH_MAX]; realpath(initialDir, pathBuffer); #endif//VORB_OS_WINDOWS rval = pathBuffer; return rval; } void pError(const char *message) { FILE *logFile = NULL; SDL_SetRelativeMouseMode(SDL_FALSE); logFile = fopen("errorlog.txt", "a+"); if (logFile != NULL){ fprintf(logFile, "*ERROR: %s \n", message); fclose(logFile); } printf("*ERROR: %s \n", message); fflush(stdout); showMessage("ERROR: " + nString(message)); } void pError(const nString& message) { FILE *logFile = NULL; SDL_SetRelativeMouseMode(SDL_FALSE); logFile = fopen("errorlog.txt", "a+"); if (logFile != NULL){ fprintf(logFile, "*ERROR: %s \n", message.c_str()); fclose(logFile); } printf("*ERROR: %s \n", message.c_str()); fflush(stdout); showMessage("ERROR: " + message); } //Checks the output of glGetError and prints an appropriate error message if needed. bool checkGlError(const nString& errorLocation) { GLenum error = glGetError(); if (error != GL_NO_ERROR) { switch (error) { case GL_INVALID_ENUM: pError("At " + errorLocation + ". Error code 1280: GL_INVALID_ENUM"); break; case GL_INVALID_VALUE: pError("At " + errorLocation + ". Error code 1281: GL_INVALID_VALUE"); break; case GL_INVALID_OPERATION: pError("At " + errorLocation + ". Error code 1282: GL_INVALID_OPERATION"); break; case GL_STACK_OVERFLOW: pError("At " + errorLocation + ". Error code 1283: GL_STACK_OVERFLOW"); break; case GL_STACK_UNDERFLOW: pError("At " + errorLocation + ". Error code 1284: GL_STACK_UNDERFLOW"); break; case GL_OUT_OF_MEMORY: pError("At " + errorLocation + ". Error code 1285: GL_OUT_OF_MEMORY"); break; case GL_INVALID_FRAMEBUFFER_OPERATION: pError("At " + errorLocation + ". Error code 1285: GL_INVALID_FRAMEBUFFER_OPERATION"); break; default: pError("At " + errorLocation + ". Error code " + std::to_string(error) + ": UNKNOWN"); break; } return true; } return false; } ================================================ FILE: SoA/Errors.h ================================================ #pragma once #include "Vorb/types.h" //yes 1, no 0 extern i32 showYesNoBox(const nString& message); extern i32 showYesNoCancelBox(const nString& message); extern void showMessage(const nString& message); extern nString getFullPath(const cString initialDir); extern void pError(const cString message); extern void pError(const nString& message); extern bool checkGlError(const nString& errorLocation); ================================================ FILE: SoA/ExposureCalcRenderStage.cpp ================================================ #include "stdafx.h" #include "ExposureCalcRenderStage.h" #include "ShaderLoader.h" #include #include #include #include #include #include #define EXPOSURE_FUNCTION_FILE "Shaders/PostProcessing/exposure.lua" #define EXPOSURE_FUNCTION_NAME "calculateExposure" ExposureCalcRenderStage::ExposureCalcRenderStage() { } ExposureCalcRenderStage::~ExposureCalcRenderStage() { } void ExposureCalcRenderStage::hook(vg::FullQuadVBO* quad, vg::GBuffer* hdrFrameBuffer, const ui32v4* viewPort, ui32 resolution) { if(!m_env) { m_env=new vscript::lua::Environment(); m_env->init(); } m_quad = quad; m_hdrFrameBuffer = hdrFrameBuffer; m_restoreViewport = viewPort; m_resolution = resolution; ui32 size = resolution; m_mipLevels = 1; while (size > 1) { m_mipLevels++; size >>= 1; } m_mipStep = 0; } void ExposureCalcRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { m_mipStep = 0; if (m_program.isCreated()) m_program.dispose(); if (m_downsampleProgram.isCreated()) m_downsampleProgram.dispose(); for (size_t i = 0; i < m_renderTargets.size(); i++) { m_renderTargets[i].dispose(); } m_renderTargets.clear(); m_env->dispose(); delete m_env; m_needsScriptLoad = true; } void ExposureCalcRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED /*= nullptr*/) { if (m_renderTargets.empty()) { m_renderTargets.resize(m_mipLevels); for (size_t i = 0; i < m_mipLevels; i++) { ui32 res = m_resolution >> i; m_renderTargets[i].setSize(res, res); m_renderTargets[i].init(vg::TextureInternalFormat::RGBA16F); } } // Lazy shader load if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/LogLuminance.frag"); m_program.use(); glUniform1i(m_program.getUniform("unTex"), 0); m_program.unuse(); } if (!m_downsampleProgram.isCreated()) { m_downsampleProgram = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/LumDownsample.frag"); m_downsampleProgram.use(); glUniform1i(m_downsampleProgram.getUniform("unTex"), 0); m_downsampleProgram.unuse(); } // Lazy script load if (m_needsScriptLoad) { m_env->run(nString(EXPOSURE_FUNCTION_FILE)); m_calculateExposure = m_env->template getScriptDelegate(EXPOSURE_FUNCTION_NAME); m_needsScriptLoad = false; } vg::GLProgram* prog = nullptr; if (m_mipStep == (int)m_mipLevels-1) { // Final Step m_renderTargets[m_mipStep].bindTexture(); m_mipStep = 1; f32v4 pixel = f32v4(0.0f); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, &pixel[0]); // LUA SCRIPT #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" m_exposure = m_calculateExposure(pixel); #pragma GCC diagnostic pop #else m_exposure = m_calculateExposure(pixel); #endif prog = &m_program; m_hdrFrameBuffer->bindGeometryTexture(0, 0); } else if (m_mipStep > 0) { prog = &m_downsampleProgram; m_renderTargets[m_mipStep].bindTexture(); m_mipStep++; } else { prog = &m_program; m_hdrFrameBuffer->bindGeometryTexture(0, 0); m_mipStep++; } glActiveTexture(GL_TEXTURE0); // If we have rendered a frame before, generate mips and get lowest level m_renderTargets[m_mipStep].use(); prog->use(); prog->enableVertexAttribArrays(); m_quad->draw(); prog->disableVertexAttribArrays(); prog->unuse(); m_renderTargets[m_mipStep].unuse(m_restoreViewport->z, m_restoreViewport->w); } ================================================ FILE: SoA/ExposureCalcRenderStage.h ================================================ /// /// ExposureCalcRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 30 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Calculates log luminance of a scene for tone mapping /// #pragma once #ifndef ExposureCalcRenderStage_h__ #define ExposureCalcRenderStage_h__ #include #include #include #include #include "IRenderStage.h" DECL_VG(class GLProgram); DECL_VG(class GLRenderTarget); DECL_VSCRIPT(template class IEnvironment; namespace lua { class Environment; }); class ExposureCalcRenderStage : public IRenderStage { public: ExposureCalcRenderStage(); ~ExposureCalcRenderStage(); /// resolution should be power of 2 void hook(vg::FullQuadVBO* quad, vg::GBuffer* hdrFrameBuffer, const ui32v4* viewPort, ui32 resolution); /// Disposes and deletes the shader and turns off visibility /// If stage does lazy init, shader will reload at next draw virtual void dispose(StaticLoadContext& context) override; /// Draws the render stage /// @pre no FBO is bound virtual void render(const Camera* camera = nullptr) override; void setFrameBuffer(vg::GBuffer* hdrFrameBuffer) { m_hdrFrameBuffer = hdrFrameBuffer; } const f32& getExposure() const { return m_exposure; } private: vg::GLProgram m_downsampleProgram; std::vector m_renderTargets; ///< All render targets vg::FullQuadVBO* m_quad = nullptr; vg::GBuffer* m_hdrFrameBuffer = nullptr; const ui32v4* m_restoreViewport; ui32 m_resolution; ui32 m_mipLevels = 1; int m_mipStep = -1; f32 m_exposure = 0.0005f; vg::GLProgram m_program; // Script for exposure calc bool m_needsScriptLoad = true; vscript::IEnvironment* m_env = nullptr; Delegate m_calculateExposure; }; #endif // ExposureCalcRenderStage_h__ ================================================ FILE: SoA/FarTerrainComponentRenderer.cpp ================================================ #include "stdafx.h" #include "FarTerrainComponentRenderer.h" #include "Camera.h" #include "PlanetGenData.h" #include "ShaderLoader.h" #include "SpaceSystemComponents.h" #include "TerrainPatchMeshManager.h" #include "VoxelSpaceUtils.h" #include #include #include FarTerrainComponentRenderer::~FarTerrainComponentRenderer() { dispose(); } void FarTerrainComponentRenderer::initGL() { if (!m_farTerrainProgram.isCreated()) { buildShaders(); } } void FarTerrainComponentRenderer::draw(const FarTerrainComponent& cmp, const Camera* camera, const f64v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent VORB_MAYBE_UNUSED, const AxisRotationComponent* arComponent, const AtmosphereComponent* aComponent) { // Get voxel position for quaternion calculation VoxelPosition3D pos; pos.pos = camera->getPosition(); pos.face = cmp.face; f64v3 relativeCameraPos = camera->getPosition() * KM_PER_VOXEL; // Calculate relative light position f64v3 relLightDir = glm::inverse(arComponent->currentOrientation) * lightDir; relLightDir = glm::inverse(VoxelSpaceUtils::calculateVoxelToSpaceQuat(pos, cmp.sphericalTerrainData->radius * VOXELS_PER_KM)) * relLightDir; // Sort meshes cmp.meshManager->sortFarMeshes(relativeCameraPos); // Draw far patches if (cmp.alpha > 0.0f) { cmp.meshManager->drawFarMeshes(relativeCameraPos, camera, m_farTerrainProgram, m_farWaterProgram, f32v3(relLightDir), glm::min(cmp.alpha, 1.0f), (f32)cmp.planetGenData->radius, zCoef, aComponent, (cmp.alpha >= 1.0f)); } } void FarTerrainComponentRenderer::dispose() { if (m_farTerrainProgram.isCreated()) m_farTerrainProgram.dispose(); if (m_farWaterProgram.isCreated()) m_farWaterProgram.dispose(); } void FarTerrainComponentRenderer::buildShaders() { m_farTerrainProgram = ShaderLoader::createProgramFromFile("Shaders/SphericalTerrain/FarTerrain.vert", "Shaders/SphericalTerrain/SphericalTerrain.frag"); // Set constant uniforms m_farTerrainProgram.use(); glUniform1i(m_farTerrainProgram.getUniform("unColorMap"), 1); glUniform1i(m_farTerrainProgram.getUniform("unGrassTexture"), 2); glUniform1i(m_farTerrainProgram.getUniform("unRockTexture"), 3); m_farTerrainProgram.unuse(); // Build water shader m_farWaterProgram = ShaderLoader::createProgramFromFile("Shaders/SphericalTerrain/FarWater.vert", "Shaders/SphericalTerrain/SphericalWater.frag"); // Set constant uniforms m_farWaterProgram.use(); glUniform1i(m_farWaterProgram.getUniform("unNormalMap"), 0); glUniform1i(m_farWaterProgram.getUniform("unColorMap"), 1); m_farWaterProgram.unuse(); } ================================================ FILE: SoA/FarTerrainComponentRenderer.h ================================================ /// /// FarTerrainComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 22 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renderer for far terrain patches /// #pragma once #ifndef FarTerrainComponentRenderer_h__ #define FarTerrainComponentRenderer_h__ #include #include class Camera; struct AtmosphereComponent; struct AxisRotationComponent; struct FarTerrainComponent; struct NamePositionComponent; struct SpaceLightComponent; struct SphericalTerrainComponent; class FarTerrainComponentRenderer { public: ~FarTerrainComponentRenderer(); void initGL(); void draw(const FarTerrainComponent& cmp, const Camera* camera, const f64v3& lightDir, const f32 zCoef, const SpaceLightComponent* spComponent, const AxisRotationComponent* arComponent, const AtmosphereComponent* aComponent); void dispose(); private: void buildShaders(); vg::GLProgram m_farTerrainProgram; vg::GLProgram m_farWaterProgram; }; #endif // FarTerrainComponentRenderer_h__ ================================================ FILE: SoA/FarTerrainComponentUpdater.cpp ================================================ #include "stdafx.h" #include "FarTerrainComponentUpdater.h" #include "FarTerrainPatch.h" #include "SpaceSystem.h" #include "SpaceSystemAssemblages.h" #include "SpaceSystemComponents.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatchMeshManager.h" #include "VoxelCoordinateSpaces.h" #include "soaUtils.h" void FarTerrainComponentUpdater::update(SpaceSystem* spaceSystem, const f64v3& cameraPos) { for (auto& it : spaceSystem->farTerrain) { FarTerrainComponent& cmp = it.second; /// Calculate camera distance f64 distance = glm::length(cameraPos); // Update fading in and out animation if (cmp.shouldFade) { cmp.alpha -= TERRAIN_ALPHA_STEP; if (cmp.alpha <= 0.0f) { continue; } } else { cmp.alpha += TERRAIN_ALPHA_STEP; if (cmp.alpha > 1.0f) cmp.alpha = 1.0f; } // Check for transitioning to a new grid face if (cmp.transitionFace != FACE_NONE) { cmp.face = cmp.transitionFace; cmp.transitionFace = FACE_NONE; if (cmp.patches) { delete[] cmp.patches; cmp.patches = nullptr; } } if (distance <= LOAD_DIST) { // In range, allocate if needed if (!cmp.patches) { initPatches(cmp, cameraPos); } else { // Check to see if the grid should shift const f64& patchWidth = (cmp.sphericalTerrainData->radius * 2.000) / FT_PATCH_ROW; i32v2 newCenter(fastFloor(cameraPos.x / patchWidth), fastFloor(cameraPos.z / patchWidth)); checkGridShift(cmp, newCenter); } // Update patches for (int i = 0; i < FT_TOTAL_PATCHES; i++) { cmp.patches[i].update(cameraPos); } } else { // Out of range, delete everything if (cmp.patches) { delete[] cmp.patches; cmp.patches = nullptr; } } } } void FarTerrainComponentUpdater::initPatches(FarTerrainComponent& cmp, const f64v3& cameraPos) { const f64& patchWidth = (cmp.sphericalTerrainData->radius * 2.000) / FT_PATCH_ROW; // Allocate top level patches cmp.patches = new FarTerrainPatch[FT_TOTAL_PATCHES]; cmp.center.x = fastFloor(cameraPos.x / patchWidth); cmp.center.y = fastFloor(cameraPos.z / patchWidth); int centerX = FT_PATCH_ROW / 2 - cmp.center.x; int centerZ = FT_PATCH_ROW / 2 - cmp.center.y; f64v2 gridPos; int index = 0; // Init all the top level patches for each of the 6 grids for (int z = 0; z < FT_PATCH_ROW; z++) { for (int x = 0; x < FT_PATCH_ROW; x++) { FarTerrainPatch& p = cmp.patches[index++]; gridPos.x = (x - centerX) * patchWidth; gridPos.y = (z - centerZ) * patchWidth; p.init(gridPos, cmp.face, 0, cmp.sphericalTerrainData, patchWidth); } } } void FarTerrainComponentUpdater::glUpdate(SpaceSystem* spaceSystem) { for (auto& it : spaceSystem->farTerrain) { if (it.second.meshManager) it.second.meshManager->update(); } } void FarTerrainComponentUpdater::checkGridShift(FarTerrainComponent& cmp, const i32v2& newCenter) { f64v2 gridPos; const f64& patchWidth = (cmp.sphericalTerrainData->radius * 2.000) / FT_PATCH_ROW; // X shift if (newCenter.x > cmp.center.x) { // +X shift // Shift center cmp.center.x++; // Destroy and re-init the leftmost column of chunks i32 gx = cmp.origin.x; for (i32 z = 0; z < FT_PATCH_ROW; z++) { i32 gz = (cmp.origin.y + z) % FT_PATCH_ROW; FarTerrainPatch& p = cmp.patches[gz * FT_PATCH_ROW + gx]; p.destroy(); gridPos.x = (cmp.center.x + FT_PATCH_ROW / 2 - 1) * patchWidth; gridPos.y = (cmp.center.y + z - FT_PATCH_ROW / 2) * patchWidth; p.init(gridPos, cmp.face, 0, cmp.sphericalTerrainData, patchWidth); } // Shift origin cmp.origin.x++; // Origin is % FT_PATCH_ROW if (cmp.origin.x >= FT_PATCH_ROW) cmp.origin.x = 0; return; } else if (newCenter.x < cmp.center.x) { // -X shift // Shift center cmp.center.x--; // Destroy and re-init the rightmost column of chunks i32 gx = (cmp.origin.x + FT_PATCH_ROW - 1) % FT_PATCH_ROW; for (i32 z = 0; z < FT_PATCH_ROW; z++) { i32 gz = (cmp.origin.y + z) % FT_PATCH_ROW; FarTerrainPatch& p = cmp.patches[gz * FT_PATCH_ROW + gx]; p.destroy(); gridPos.x = (cmp.center.x - FT_PATCH_ROW / 2) * patchWidth; gridPos.y = (cmp.center.y + z - FT_PATCH_ROW / 2) * patchWidth; p.init(gridPos, cmp.face, 0, cmp.sphericalTerrainData, patchWidth); } // Shift origin cmp.origin.x--; // Origin is % FT_PATCH_ROW if (cmp.origin.x < 0) cmp.origin.x = FT_PATCH_ROW - 1; return; } // Z shift if (newCenter.y > cmp.center.y) { // +Z shift // Shift center cmp.center.y++; // Destroy and re-init the leftmost column of chunks i32 gz = cmp.origin.y; for (int x = 0; x < FT_PATCH_ROW; x++) { int gx = (cmp.origin.x + x) % FT_PATCH_ROW; FarTerrainPatch& p = cmp.patches[gz * FT_PATCH_ROW + gx]; p.destroy(); gridPos.x = (cmp.center.x + x - FT_PATCH_ROW / 2) * patchWidth; gridPos.y = (cmp.center.y + FT_PATCH_ROW / 2 - 1) * patchWidth; p.init(gridPos, cmp.face, 0, cmp.sphericalTerrainData, patchWidth); } // Shift origin cmp.origin.y++; // Origin is % FT_PATCH_ROW if (cmp.origin.y >= FT_PATCH_ROW) cmp.origin.y = 0; } else if (newCenter.y < cmp.center.y) { // -Z shift // Shift center cmp.center.y--; // Destroy and re-init the rightmost column of chunks i32 gz = (cmp.origin.y + FT_PATCH_ROW - 1) % FT_PATCH_ROW; for (i32 x = 0; x < FT_PATCH_ROW; x++) { int gx = (cmp.origin.x + x) % FT_PATCH_ROW; FarTerrainPatch& p = cmp.patches[gz * FT_PATCH_ROW + gx]; p.destroy(); gridPos.x = (cmp.center.x + x - FT_PATCH_ROW / 2) * patchWidth; gridPos.y = (cmp.center.y - FT_PATCH_ROW / 2) * patchWidth; p.init(gridPos, cmp.face, 0, cmp.sphericalTerrainData, patchWidth); } // Shift origin cmp.origin.y--; // Origin is % FT_PATCH_ROW if (cmp.origin.y < 0) cmp.origin.y = FT_PATCH_ROW - 1; } } ================================================ FILE: SoA/FarTerrainComponentUpdater.h ================================================ /// /// FarTerrainComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 12 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updater for far terrain /// #pragma once #ifndef FarTerrainComponentUpdater_h__ #define FarTerrainComponentUpdater_h__ class SpaceSystem; struct FarTerrainComponent; #include "TerrainPatch.h" #include "VoxelCoordinateSpaces.h" #include "SphericalTerrainComponentUpdater.h" #include #define FT_PATCH_ROW 16 #define NUM_FACES 6 const int FT_TOTAL_PATCHES = (FT_PATCH_ROW * FT_PATCH_ROW); class FarTerrainComponentUpdater { public: void update(SpaceSystem* spaceSystem, const f64v3& cameraPos); /// Updates openGL specific stuff. Call on render thread void glUpdate(SpaceSystem* spaceSystem); private: void initPatches(FarTerrainComponent& cmp, const f64v3& cameraPos); // Checks and possibly shifts the far terrain patch grid to always center on the player void checkGridShift(FarTerrainComponent& cmp, const i32v2& newCenter); }; #endif // FarTerrainComponentUpdater_h__ ================================================ FILE: SoA/FarTerrainPatch.cpp ================================================ #include "stdafx.h" #include "FarTerrainPatch.h" #include #include #include #include "Camera.h" #include "RenderUtils.h" #include "TerrainPatchMesher.h" #include "VoxelCoordinateSpaces.h" #include "VoxelSpaceConversions.h" #include "soaUtils.h" FarTerrainPatch::~FarTerrainPatch() { destroy(); } void FarTerrainPatch::init(const f64v2& gridPosition, WorldCubeFace cubeFace, int lod, const TerrainPatchData* sphericalTerrainData, f64 width) { m_gridPos = gridPosition; m_cubeFace = cubeFace; m_lod = lod; m_terrainPatchData = sphericalTerrainData; m_width = width; // Get world position and bounding box m_aabbPos = f32v3(m_gridPos.x, 0, m_gridPos.y); m_aabbDims = f32v3(m_width, 0, m_width); } void FarTerrainPatch::update(const f64v3& cameraPos) { // TODO(Matthew): This value is never used, check the function is behaving as expected. //f64v3 closestPoint = calculateClosestPointAndDist(cameraPos); if (m_children) { if (m_distance > m_width * DIST_MAX) { if (!m_mesh) { requestMesh(false); } if (hasMesh()) { // Out of range, kill children delete[] m_children; m_children = nullptr; } } else if (m_mesh) { // In range, but we need to remove our mesh. // Check to see if all children are renderable bool deleteMesh = true; for (int i = 0; i < 4; i++) { if (!m_children[i].isRenderable()) { deleteMesh = false; break; } } if (deleteMesh) { // Children are renderable, free mesh. // Render thread will deallocate. m_mesh->m_shouldDelete = true; m_mesh = nullptr; } } } else if (m_lod < PATCH_MAX_LOD && m_distance < m_width * DIST_MIN && m_width > MIN_SIZE) { m_children = new FarTerrainPatch[4]; // Segment into 4 children for (int z = 0; z < 2; z++) { for (int x = 0; x < 2; x++) { m_children[(z << 1) + x].init(m_gridPos + f64v2((m_width / 2.0) * x, (m_width / 2.0) * z), m_cubeFace, m_lod + 1, m_terrainPatchData, m_width / 2.0); } } } else if (!m_mesh) { requestMesh(false); } // Recursively update children if they exist if (m_children) { for (int i = 0; i < 4; i++) { m_children[i].update(cameraPos); } } } bool FarTerrainPatch::isOverHorizon(const f64v3 &relCamPos, const f64v3 &point, f64 planetRadius) { const f64 DELTA = 0.1; // Position of point relative to sphere tip f64v3 spherePoint = point - f64v3(relCamPos.x, -planetRadius, relCamPos.z); // We assume the camera is at the tip of the sphere f64v3 sphereCamPos(0, relCamPos.y + planetRadius, 0); f64 camHeight = glm::length(sphereCamPos); f64v3 normalizedCamPos = sphereCamPos / camHeight; // Limit the camera depth if (camHeight < planetRadius + 1.0) camHeight = planetRadius + 1.0; f64 horizonAngle = acos(planetRadius / camHeight); f64 lodAngle = acos(glm::dot(normalizedCamPos, glm::normalize(spherePoint))); if (lodAngle >= horizonAngle + DELTA) { return true; } return false; } ================================================ FILE: SoA/FarTerrainPatch.h ================================================ /// /// FarTerrainPatch.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Defines a class for a patch of far-terrain /// #pragma once #ifndef FarTerrainPatch_h__ #define FarTerrainPatch_h__ #include "TerrainPatch.h" // TODO(Ben): Linear fade to prevent LOD popping class FarTerrainPatch : public TerrainPatch { public: FarTerrainPatch() {}; ~FarTerrainPatch(); /// Initializes the patch /// @param gridPosition: Position on the 2d face grid /// @param sphericalTerrainData: Shared data /// @param width: Width of the patch in KM void init(const f64v2& gridPosition, WorldCubeFace cubeFace, int lod, const TerrainPatchData* sphericalTerrainData, f64 width) override; /// Updates the patch /// @param cameraPos: Position of the camera void update(const f64v3& cameraPos) override; /// Checks if the point is over the horizon /// @param relCamPos: Relative observer position /// @param point: The point to check /// @param planetRadius: Radius of the planet static bool isOverHorizon(const f64v3 &relCamPos, const f64v3 &point, f64 planetRadius); }; #endif // FarTerrainPatch_h__ ================================================ FILE: SoA/Flora.cpp ================================================ #include "stdafx.h" #include "Flora.h" KEG_ENUM_DEF(FloraInterpType, FloraInterpType, e) { e.addValue("linear", FloraInterpType::LINEAR); e.addValue("lin", FloraInterpType::LINEAR); e.addValue("hermite", FloraInterpType::HERMITE); e.addValue("herm", FloraInterpType::HERMITE); e.addValue("cosine", FloraInterpType::COSINE); e.addValue("cos", FloraInterpType::COSINE); e.addValue("sine", FloraInterpType::SINE); e.addValue("sin", FloraInterpType::SINE); } KEG_ENUM_DEF(TreeLeafType, TreeLeafType, e) { e.addValue("none", TreeLeafType::NONE); e.addValue("round", TreeLeafType::ROUND); e.addValue("pine", TreeLeafType::PINE); e.addValue("mushroom", TreeLeafType::MUSHROOM); } KEG_ENUM_DEF(FloraDir, FloraDir, e) { e.addValue("up", FloraDir::UP); e.addValue("side", FloraDir::SIDE); e.addValue("down", FloraDir::DOWN); } ================================================ FILE: SoA/Flora.h ================================================ /// /// Flora.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 15 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Flora struct definitions. /// #pragma once #ifndef Flora_h__ #define Flora_h__ #include #include #define FLORA_ID_NONE 0xFFFFu // Both trees and flora share FloraID typedef ui16 FloraID; /* TreeType is a breed of tree, TreeData is an individual tree.*/ enum class FloraInterpType { LINEAR, HERMITE, COSINE, SINE }; KEG_ENUM_DECL(FloraInterpType); enum class TreeLeafType { NONE, ROUND, PINE, MUSHROOM }; KEG_ENUM_DECL(TreeLeafType); struct TreeTypeFruitProperties { FloraID flora = FLORA_ID_NONE; Range chance; }; struct TreeFruitProperties { FloraID flora = FLORA_ID_NONE; f32 chance; }; struct TreeTypeLeafProperties { TreeLeafType type; TreeTypeFruitProperties fruitProps; // Mutually exclusive union based on type union { UNIONIZE(struct { Range vRadius; Range hRadius; ui16 blockID; } round;); UNIONIZE(struct { Range oRadius; Range iRadius; Range period; ui16 blockID; } pine;); UNIONIZE(struct { Range tvRadius; Range thRadius; Range bvRadius; Range bhRadius; Range bLength; Range capWidth; Range gillWidth; ui16 gillBlockID; ui16 capBlockID; FloraInterpType interp; } mushroom;); }; }; struct TreeLeafProperties { TreeLeafType type; TreeFruitProperties fruitProps; // Mutually exclusive union based on type union { UNIONIZE(struct { ui16 vRadius; ui16 hRadius; ui16 blockID; } round;); UNIONIZE(struct { ui16 oRadius; ui16 iRadius; ui16 period; ui16 blockID; } pine;); UNIONIZE(struct { ui16 tvRadius; ui16 thRadius; ui16 bvRadius; ui16 bhRadius; ui16 bLength; ui16 capWidth; ui16 gillWidth; ui16 gillBlockID; ui16 capBlockID; FloraInterpType interp; } mushroom;); }; }; struct TreeTypeBranchProperties { Range coreWidth; Range barkWidth; Range widthFalloff; Range branchChance; Range angle; Range subBranchAngle; Range changeDirChance; ui16 coreBlockID = 0; ui16 barkBlockID = 0; TreeTypeFruitProperties fruitProps; TreeTypeLeafProperties leafProps; }; struct TreeBranchProperties { f32 branchChance; f32 widthFalloff; f32 changeDirChance; ui16 coreWidth; ui16 barkWidth; ui16 coreBlockID = 0; ui16 barkBlockID = 0; Range angle; Range subBranchAngle; TreeFruitProperties fruitProps; TreeLeafProperties leafProps; }; struct TreeTypeTrunkProperties { f32 loc; Range coreWidth; Range barkWidth; Range branchChance; Range changeDirChance; Range> slope; ui16 coreBlockID = 0; ui16 barkBlockID = 0; FloraInterpType interp; TreeTypeFruitProperties fruitProps; TreeTypeLeafProperties leafProps; TreeTypeBranchProperties branchProps; }; struct TreeTrunkProperties { f32 loc; ui16 coreWidth; ui16 barkWidth; f32 branchChance; f32 changeDirChance; i32 slope; ui16 coreBlockID; ui16 barkBlockID; FloraInterpType interp; TreeFruitProperties fruitProps; TreeLeafProperties leafProps; TreeBranchProperties branchProps; }; struct TreeTypeBranchVolumeProperties { Range height; Range hRadius; Range vRadius; Range points; }; struct BranchVolumeProperties { ui16 height; ui16 hRadius; ui16 vRadius; ui16 points; f32 infRadius; }; struct NTreeType { // All ranges are for scaling between baby tree and adult tree Range height; Range branchPoints; Range branchStep; Range killMult; Range infRadius; std::vector branchVolumes; // Data points for trunk properties. Properties get interpolated between these from // base of tree to top of tree. std::vector trunkProps; }; // Specification for an individual tree struct TreeData { f32 age; ///< 0 - 1 ui16 height; ui16 branchPoints; ui16 branchStep; ui16 killMult; ui16 currentDir; f32 infRadius; std::vector branchVolumes; std::vector trunkProps; }; enum class FloraDir { UP, SIDE, DOWN }; KEG_ENUM_DECL(FloraDir); struct FloraType { ui16 block; Range height; Range slope; Range dSlope; FloraDir dir; const FloraType* nextFlora = nullptr; }; // Specification for individual flora struct FloraData { ui16 block; ui16 height; ui16 dir; ui16 slope; ui16 dSlope; const FloraType* nextFlora; }; #endif // Flora_h__ ================================================ FILE: SoA/FloraGenerator.cpp ================================================ #include "stdafx.h" #include "FloraGenerator.h" #define X_1 0x100000 #define Y_1 0x400 #define Z_1 0x1 #define OUTER_SKIP_MOD 5 #ifdef VORB_OS_WINDOWS #pragma region helpers #endif//VORB_OS_WINDOWS /************************************************************************/ /* Helper Functions */ /************************************************************************/ /* These functions allow us to move in chunk-space without branching. */ /************************************************************************/ // Adds a positive offset inline void offsetPositive(int& x, int x1, ui32& chunkOffset, int offset) { x += offset; chunkOffset += x1 * (x / CHUNK_WIDTH); x &= 0x1f; // Modulo 32 } inline void offsetNegative(int& x, int x1, ui32& chunkOffset, int offset) { // We are inverting and treating negative as positive here so modulus works // without branching x = (CHUNK_WIDTH_M1 - x) + offset; chunkOffset -= x1 * (x / CHUNK_WIDTH); // Modulo 32 and invert back to positive x = CHUNK_WIDTH_M1 - (x & 0x1f); } inline void offsetNegative(int& x, int& y, int x1, int y1, ui32& chunkOffset, int offset) { // We are inverting and treating negative as positive here so modulus works // without branching x = (CHUNK_WIDTH_M1 - x) + offset; y = (CHUNK_WIDTH_M1 - y) + offset; chunkOffset -= x1 * (x / CHUNK_WIDTH); chunkOffset -= y1 * (y / CHUNK_WIDTH); // Modulo 32 and invert back to positive x = CHUNK_WIDTH_M1 - (x & 0x1f); y = CHUNK_WIDTH_M1 - (y & 0x1f); } inline void offsetNegative(int& x, int& y, int& z, ui32& chunkOffset, int offset) { // We are inverting and treating negative as positive here so modulus works // without branching x = (CHUNK_WIDTH_M1 - x) + offset; y = (CHUNK_WIDTH_M1 - y) + offset; z = (CHUNK_WIDTH_M1 - z) + offset; chunkOffset -= X_1 * (x / CHUNK_WIDTH); chunkOffset -= Y_1 * (y / CHUNK_WIDTH); chunkOffset -= Z_1 * (z / CHUNK_WIDTH); // Modulo 32 and invert back to positive x = CHUNK_WIDTH_M1 - (x & 0x1f); y = CHUNK_WIDTH_M1 - (y & 0x1f); z = CHUNK_WIDTH_M1 - (z & 0x1f); } inline void addChunkOffset(i32v2& pos, ui32& chunkOffset) { // Modify chunk offset chunkOffset += X_1 * (pos.x / CHUNK_WIDTH); chunkOffset += Z_1 * (pos.y / CHUNK_WIDTH); pos &= 0x1f; // Modulo 32 } inline void addChunkOffset(i32v3& pos, ui32& chunkOffset) { // Modify chunk offset chunkOffset += X_1 * (pos.x / CHUNK_WIDTH); chunkOffset += Y_1 * (pos.y / CHUNK_WIDTH); chunkOffset += Z_1 * (pos.z / CHUNK_WIDTH); // Modulo 32 pos &= 0x1f; // Modulo 32 } inline void offsetAxis(int& x, int x1, ui32& chunkOffset, int offset) { if (offset < 0) { x = (CHUNK_WIDTH_M1 - x) - offset; chunkOffset -= x1 * (x / CHUNK_WIDTH); x = CHUNK_WIDTH_M1 - (x & 0x1f); } else { x = x + offset; chunkOffset += x1 * (x / CHUNK_WIDTH); x &= 0x1f; } } // Offsets by a floating point position inline void offsetByPos(int& x, int& y, int& z, ui32& chunkOffset, const f32v3& pos) { if (pos.x < 0.0f) { x = (CHUNK_WIDTH_M1 - x) - fastFloor(pos.x); chunkOffset -= X_1 * (x / CHUNK_WIDTH); x = CHUNK_WIDTH_M1 - (x & 0x1f); } else { x = x + (int)pos.x; chunkOffset += X_1 * (x / CHUNK_WIDTH); x &= 0x1f; } if (pos.y < 0.0f) { y = (CHUNK_WIDTH_M1 - y) - fastFloor(pos.y); chunkOffset -= Y_1 * (y / CHUNK_WIDTH); y = CHUNK_WIDTH_M1 - (y & 0x1f); } else { y = y + (int)pos.y; chunkOffset += Y_1 * (y / CHUNK_WIDTH); y &= 0x1f; } if (pos.z < 0.0f) { z = (CHUNK_WIDTH_M1 - z) - fastFloor(pos.z); chunkOffset -= Z_1 * (z / CHUNK_WIDTH); z = CHUNK_WIDTH_M1 - (z & 0x1f); } else { z = z + (int)pos.z; chunkOffset += Z_1 * (z / CHUNK_WIDTH); z &= 0x1f; } } inline void offsetByPos(int& x, int& y, int& z, ui32& chunkOffset, const i32v3& pos) { if (pos.x < 0) { x = (CHUNK_WIDTH_M1 - x) - pos.x; chunkOffset -= X_1 * (x / CHUNK_WIDTH); x = CHUNK_WIDTH_M1 - (x & 0x1f); } else { x = x + pos.x; chunkOffset += X_1 * (x / CHUNK_WIDTH); x &= 0x1f; } if (pos.y < 0) { y = (CHUNK_WIDTH_M1 - y) - pos.y; chunkOffset -= Y_1 * (y / CHUNK_WIDTH); y = CHUNK_WIDTH_M1 - (y & 0x1f); } else { y = y + pos.y; chunkOffset += Y_1 * (y / CHUNK_WIDTH); y &= 0x1f; } if (pos.z < 0) { z = (CHUNK_WIDTH_M1 - z) - pos.z; chunkOffset -= Z_1 * (z / CHUNK_WIDTH); z = CHUNK_WIDTH_M1 - (z & 0x1f); } else { z = z + pos.z; chunkOffset += Z_1 * (z / CHUNK_WIDTH); z &= 0x1f; } } /************************************************************************/ /* End Helper Functions */ /************************************************************************/ #ifdef VORB_OS_WINDOWS #pragma endregion #endif//VORB_OS_WINDOWS // Smooths an input factor on the range 0-1 inline void smoothInterpFactor(f32& l, const FloraInterpType& type) { switch (type) { case FloraInterpType::HERMITE: l = hermite(l); break; case FloraInterpType::COSINE: // TODO(Ben): cos lookup table l = (f32)(1.0 - cos((f64)l * M_PI_2)); break; case FloraInterpType::SINE: // TODO(Ben): sin lookup table l = (f32)(sin((f64)l * M_PI_2)); break; default: break; } } void FloraGenerator::generateChunkFlora(const Chunk* chunk, const PlanetHeightData* heightData, OUT std::vector& fNodes, OUT std::vector& wNodes) { // Iterate all block indices where flora must be generated for (ui16 blockIndex : chunk->floraToGenerate) { // Get position m_center.x = blockIndex & 0x1F; // & 0x1F = % 32 m_center.y = blockIndex / CHUNK_LAYER; m_center.z = (blockIndex & 0x3FF) / CHUNK_WIDTH; // & 0x3FF = % 1024 const PlanetHeightData& hd = heightData[blockIndex & 0x3FF]; // & 0x3FF = % CHUNK_LAYER const Biome* b = hd.biome; // Seed the generator const VoxelPosition3D& vpos = chunk->getVoxelPosition(); m_rGen.seed(vpos.pos.x + m_center.x, vpos.pos.y + m_center.y, vpos.pos.z + m_center.z); // Get age f32 age = (f32)m_rGen.genlf(); // Determine which to generate if (hd.flora < b->flora.size()) { // It's a flora generateFlora(b->flora[hd.flora].data, age, fNodes, wNodes, NO_CHUNK_OFFSET, blockIndex); } else { // It's a tree generateTree(b->trees[hd.flora - b->flora.size()].data, age, fNodes, wNodes, b->genData, NO_CHUNK_OFFSET, blockIndex); } } } #ifdef VORB_OS_WINDOWS #pragma region lerping #endif//VORB_OS_WINDOWS // Lerps and rounds #define LERP_UI16(var) FastConversion::floor((b.var - a.var) * l + a.var + 0.5f) #define LERP_I32(var) fastFloor((f32)(b.var - a.var) * l + (f32)a.var + 0.5f) inline void lerpFruitProperties(TreeFruitProperties& rvProps, const TreeFruitProperties& a, const TreeFruitProperties& b, f32 l) { rvProps.chance = lerp(a.chance, b.chance, l); if (l < 0.5) { rvProps.flora = a.flora; } else { rvProps.flora = b.flora; } } inline void lerpLeafProperties(TreeLeafProperties& rvProps, const TreeLeafProperties& a, const TreeLeafProperties& b, f32 l) { const TreeLeafProperties* blockP; if (l < 0.5f) { blockP = &a; } else { blockP = &b; } rvProps.type = blockP->type; switch (rvProps.type) { case TreeLeafType::ROUND: rvProps.round.blockID = blockP->round.blockID; if (a.type == b.type) { rvProps.round.vRadius = LERP_UI16(round.vRadius); rvProps.round.hRadius = LERP_UI16(round.hRadius); } else { rvProps.round.vRadius = blockP->round.vRadius; rvProps.round.hRadius = blockP->round.hRadius; } break; case TreeLeafType::PINE: rvProps.pine.blockID = blockP->pine.blockID; if (a.type == b.type) { rvProps.pine.oRadius = LERP_UI16(pine.oRadius); rvProps.pine.iRadius = LERP_UI16(pine.iRadius); rvProps.pine.period = LERP_UI16(pine.period); } else { rvProps.pine.oRadius = blockP->pine.oRadius; rvProps.pine.iRadius = blockP->pine.iRadius; rvProps.pine.period = blockP->pine.period; } break; case TreeLeafType::MUSHROOM: rvProps.mushroom.capBlockID = blockP->mushroom.capBlockID; rvProps.mushroom.gillBlockID = blockP->mushroom.gillBlockID; rvProps.mushroom.interp = blockP->mushroom.interp; if (a.type == b.type) { rvProps.mushroom.tvRadius = LERP_UI16(mushroom.tvRadius); rvProps.mushroom.thRadius = LERP_UI16(mushroom.thRadius); rvProps.mushroom.bvRadius = LERP_UI16(mushroom.bvRadius); rvProps.mushroom.bhRadius = LERP_UI16(mushroom.bhRadius); rvProps.mushroom.bLength = LERP_UI16(mushroom.bLength); rvProps.mushroom.capWidth = LERP_UI16(mushroom.capWidth); rvProps.mushroom.gillWidth = LERP_UI16(mushroom.gillWidth); } else { rvProps.mushroom.tvRadius = blockP->mushroom.tvRadius; rvProps.mushroom.thRadius = blockP->mushroom.thRadius; rvProps.mushroom.bvRadius = blockP->mushroom.bvRadius; rvProps.mushroom.bhRadius = blockP->mushroom.bhRadius; rvProps.mushroom.bLength = blockP->mushroom.bLength; rvProps.mushroom.capWidth = blockP->mushroom.capWidth; rvProps.mushroom.gillWidth = blockP->mushroom.gillWidth; } break; default: break; } lerpFruitProperties(rvProps.fruitProps, a.fruitProps, b.fruitProps, l); } inline void lerpBranchProperties(TreeBranchProperties& rvProps, const TreeBranchProperties& a, const TreeBranchProperties& b, f32 l) { // Block IDs if (l < 0.5f) { rvProps.barkBlockID = a.barkBlockID; rvProps.coreBlockID = a.coreBlockID; } else { rvProps.barkBlockID = b.barkBlockID; rvProps.coreBlockID = b.coreBlockID; } // Lerp the rest rvProps.coreWidth = LERP_UI16(coreWidth); rvProps.barkWidth = LERP_UI16(barkWidth); rvProps.branchChance = lerp(a.branchChance, b.branchChance, l); rvProps.changeDirChance = lerp(a.changeDirChance, b.changeDirChance, l); rvProps.widthFalloff = lerp(a.widthFalloff, b.widthFalloff, l); if (rvProps.widthFalloff == 0.0f) rvProps.widthFalloff = 0.1f; // Avoid div by zero rvProps.angle.min = lerp(a.angle.min, b.angle.min, l); rvProps.angle.max = lerp(a.angle.max, b.angle.max, l); rvProps.subBranchAngle.min = lerp(a.subBranchAngle.min, b.subBranchAngle.min, l); rvProps.subBranchAngle.max = lerp(a.subBranchAngle.max, b.subBranchAngle.max, l); lerpLeafProperties(rvProps.leafProps, a.leafProps, b.leafProps, l); lerpFruitProperties(rvProps.fruitProps, a.fruitProps, b.fruitProps, l); } inline void lerpTrunkProperties(TreeTrunkProperties& rvProps, const TreeTrunkProperties& a, const TreeTrunkProperties& b, f32 heightRatio) { // TODO(Ben): Other interpolation types f32 l = (heightRatio - a.loc) / (b.loc - a.loc); // Hermite interpolation for smooth curve smoothInterpFactor(l, a.interp); rvProps.interp = a.interp; // Block IDs if (l < 0.5f) { rvProps.barkBlockID = a.barkBlockID; rvProps.coreBlockID = a.coreBlockID; } else { rvProps.barkBlockID = b.barkBlockID; rvProps.coreBlockID = b.coreBlockID; } // Lerp the rest rvProps.coreWidth = LERP_UI16(coreWidth); rvProps.barkWidth = LERP_UI16(barkWidth); rvProps.branchChance = lerp(a.branchChance, b.branchChance, l); rvProps.changeDirChance = lerp(a.changeDirChance, b.changeDirChance, l); rvProps.slope = LERP_I32(slope); lerpBranchProperties(rvProps.branchProps, a.branchProps, b.branchProps, l); lerpLeafProperties(rvProps.leafProps, a.leafProps, b.leafProps, l); lerpFruitProperties(rvProps.fruitProps, a.fruitProps, b.fruitProps, l); } struct DirLookup { int axis; int one; int sign; }; const DirLookup DIR_AXIS_LOOKUP[4] = { {0, X_1, -1}, {2, Z_1, -1}, {0, X_1, 1}, {2, Z_1, 1} }; void FloraGenerator::generateTree(const NTreeType* type, f32 age, OUT std::vector& fNodes, OUT std::vector& wNodes, const PlanetGenData* genData, ui32 chunkOffset /*= NO_CHUNK_OFFSET*/, ui16 blockIndex /*= 0*/) { // Get the properties for this tree // TODO(Ben): Temp m_genData = genData; age = 1.0f; m_currChunkOff = 0; generateTreeProperties(type, age, m_treeData); m_nodeFields.reserve(m_treeData.height / CHUNK_WIDTH + 3); m_nodeFieldsMap.reserve(200); // Get handles m_wNodes = &wNodes; m_fNodes = &fNodes; f32v3 m_startPos = f32v3(m_center) + f32v3(CHUNK_WIDTH * getChunkXOffset(chunkOffset), CHUNK_WIDTH * getChunkYOffset(chunkOffset), CHUNK_WIDTH * getChunkZOffset(chunkOffset)); // Interpolated trunk properties TreeTrunkProperties lerpedTrunkProps; const TreeTrunkProperties* trunkProps = nullptr; { // Generate the trunk int scNodeStep = m_treeData.height / m_treeData.branchPoints; if (scNodeStep == 0) scNodeStep = INT_MAX; // Prevent div by 0 int scNodeOffset = m_treeData.height % scNodeStep; ui32 pointIndex = 0; for (m_h = 0; m_h < m_treeData.height; ++m_h) { // Get height ratio for interpolation purposes f32 heightRatio = (f32)m_h / m_treeData.height; // Do interpolation along data points if (pointIndex < m_treeData.trunkProps.size() - 1) { if (heightRatio > m_treeData.trunkProps[pointIndex + 1].loc) { ++pointIndex; if (pointIndex < m_treeData.trunkProps.size() - 1) { lerpTrunkProperties(lerpedTrunkProps, m_treeData.trunkProps[pointIndex], m_treeData.trunkProps[pointIndex + 1], heightRatio); trunkProps = &lerpedTrunkProps; } else { // Don't need to interpolate if we are at the last data point trunkProps = &m_treeData.trunkProps.back(); } } else { lerpTrunkProperties(lerpedTrunkProps, m_treeData.trunkProps[pointIndex], m_treeData.trunkProps[pointIndex + 1], heightRatio); trunkProps = &lerpedTrunkProps; } } else { // Don't need to interpolate if we are at the last data point trunkProps = &m_treeData.trunkProps.back(); } // Check for potential branch point m_hasStoredTrunkProps = false; if ((m_h + scNodeOffset) % scNodeStep == 0) { f32 width = (f32)(trunkProps->branchProps.coreWidth + trunkProps->branchProps.barkWidth); if (width > 0.0f) { m_scNodes.emplace_back(m_scRayNodes.size()); m_scRayNodes.emplace_back(f32v3(m_center) + f32v3(CHUNK_WIDTH * getChunkXOffset(chunkOffset), CHUNK_WIDTH * getChunkYOffset(chunkOffset), CHUNK_WIDTH * getChunkZOffset(chunkOffset)), SC_NO_PARENT, m_scTrunkProps.size()); m_scTrunkProps.push_back(*trunkProps); m_hasStoredTrunkProps = true; } } // Build the trunk slice makeTrunkSlice(chunkOffset, *trunkProps); // Move up offsetPositive(m_center.y, Y_1, chunkOffset, 1); // Check for dir chance if (m_rGen.genlf() <= trunkProps->changeDirChance) { m_treeData.currentDir = m_rGen.gen() & 3; // & 3 == % 4 } // Move sideways with slope if needed if (m_h % trunkProps->slope == (unsigned int)(trunkProps->slope - 1)) { // Place a block so we don't have any floating parts when width is 1 if (trunkProps->coreWidth + trunkProps->barkWidth == 1) { if (trunkProps->coreWidth) { ui16 blockIndex = (ui16)(m_center.x + m_center.y * CHUNK_LAYER + m_center.z * CHUNK_WIDTH); tryPlaceNode(m_wNodes, 3, trunkProps->coreBlockID, blockIndex, chunkOffset); } else { ui16 blockIndex = (ui16)(m_center.x + m_center.y * CHUNK_LAYER + m_center.z * CHUNK_WIDTH); tryPlaceNode(m_wNodes, 3, trunkProps->barkBlockID, blockIndex, chunkOffset); } } const DirLookup& dir = DIR_AXIS_LOOKUP[m_treeData.currentDir]; offsetAxis(m_center[dir.axis], dir.one, chunkOffset, dir.sign); } } } if (trunkProps->leafProps.type == TreeLeafType::MUSHROOM) { generateMushroomCap(chunkOffset, m_center.x, m_center.y, m_center.z, trunkProps->leafProps); } // Branches if (m_treeData.branchVolumes.size()) { spaceColonization(m_startPos); std::vector().swap(m_scNodes); } // Generate deferred branches so they don't conflict with space colonization for (auto& it : m_branchesToGenerate) { TreeBranchProperties& bp = m_scTrunkProps[it.trunkPropsIndex].branchProps; // Defer branching so it doesn't conflict with SC f32 angle = (f32)(m_rGen.genlf() * (bp.angle.max - bp.angle.min) + bp.angle.min); // Interpolate the angle f32v3 dir = glm::normalize(lerp(glm::normalize(f32v3(it.dx, 0.0f, it.dz)), f32v3(0.0f, 1.0f, 0.0f), angle / M_PI_2F)); f32 width = (f32)(bp.coreWidth + bp.barkWidth); f32 length = width / bp.widthFalloff; // Determine float position f32v3 pos; pos.x = (f32)((it.blockIndex & 0x1F) + getChunkXOffset(it.chunkOffset) * CHUNK_WIDTH); // & 0x1F = % 32 pos.y = (f32)(it.blockIndex / CHUNK_LAYER + getChunkYOffset(it.chunkOffset) * CHUNK_WIDTH); pos.z = (f32)((it.blockIndex & 0x3FF) / CHUNK_WIDTH + getChunkZOffset(it.chunkOffset) * CHUNK_WIDTH); // & 0x3FF = % 1024 // Add root ui16 parent = m_scRayNodes.size(); m_scRayNodes.emplace_back(pos, SC_NO_PARENT, it.trunkPropsIndex); // Determine chain length int numSegments = (int)(length * bp.changeDirChance); numSegments++; length /= numSegments; // Create branch chain int i = 0; while (true) { pos += dir * length; m_scRayNodes.emplace_back(pos, parent, it.trunkPropsIndex); // Check end condition if (++i == numSegments) break; // Get new dir newDirFromAngle(dir, bp.angle.min, bp.angle.max); parent = m_scRayNodes.size() - 1; if (parent >= 32767) { break; } } // Last node is leaf m_scLeafSet.insert(m_scRayNodes.size() - 1); } // Place nodes for branches if (m_scRayNodes.size()) { generateSCBranches(); std::vector().swap(m_scRayNodes); std::set().swap(m_scLeafSet); } // Place leaves last to prevent node overlap for (auto& it : m_leavesToPlace) { m_center.x = blockIndex & 0x1F; // & 0x1F = % 32 m_center.y = blockIndex / CHUNK_LAYER; m_center.z = (blockIndex & 0x3FF) / CHUNK_WIDTH; // & 0x3FF = % 1024 generateLeaves(it.chunkOffset, it.blockIndex & 0x1F, it.blockIndex / CHUNK_LAYER, (it.blockIndex & 0x3FF) / CHUNK_WIDTH, *it.leafProps); } // Clear container memory std::vector().swap(m_leavesToPlace); std::vector().swap(m_branchesToGenerate); std::vector().swap(m_scTrunkProps); std::unordered_map().swap(m_nodeFieldsMap); std::vector().swap(m_nodeFields); } void FloraGenerator::generateFlora(const FloraType* type, f32 age, OUT std::vector& fNodes, OUT std::vector& wNodes VORB_UNUSED, ui32 chunkOffset /*= NO_CHUNK_OFFSET*/, ui16 blockIndex /*= 0*/) { FloraData data; generateFloraProperties(type, age, data); // Get position int x = blockIndex & 0x1F; // & 0x1F = % 32 int y = blockIndex / CHUNK_LAYER; int z = (blockIndex & 0x3FF) / CHUNK_WIDTH; // & 0x3FF = % 1024 do { if (data.dir == TREE_UP) { for (m_h = 0; m_h < data.height; ++m_h) { fNodes.emplace_back(data.block, blockIndex, chunkOffset); // Move up offsetPositive(y, Y_1, chunkOffset, 1); blockIndex = x + y * CHUNK_LAYER + z * CHUNK_WIDTH; } } else if (data.dir == TREE_DOWN) { for (m_h = 0; m_h < data.height; ++m_h) { fNodes.emplace_back(data.block, blockIndex, chunkOffset); // Move up offsetNegative(y, Y_1, chunkOffset, 1); blockIndex = x + y * CHUNK_LAYER + z * CHUNK_WIDTH; } } else { // TODO(Ben): Implement } // Go on to sub flora if one exists if (data.nextFlora) { generateFloraProperties(data.nextFlora, age, data); } else { break; } } while (true); } #undef LERP_UI16 #undef LERP_I32 #ifdef VORB_OS_WINDOWS #pragma endregion #pragma region age_lerping #endif//VORB_OS_WINDOWS #define AGE_LERP_F32(var) lerp(var.max, var.min, age) // Lerps and rounds #define AGE_LERP_I32(var) fastFloor((f32)(var.max - var.min) * age + (f32)var.min + 0.5f) #define AGE_LERP_UI16(var) FastConversion::floor((f32)(var.max - var.min) * age + (f32)var.min + 0.5f) inline void setFruitProps(TreeFruitProperties& fruitProps, const TreeTypeFruitProperties& typeProps, f32 age) { fruitProps.chance = AGE_LERP_F32(typeProps.chance); fruitProps.flora = typeProps.flora; } inline void setLeafProps(TreeLeafProperties& leafProps, const TreeTypeLeafProperties& typeProps, f32 age) { setFruitProps(leafProps.fruitProps, typeProps.fruitProps, age); leafProps.type = typeProps.type; switch (leafProps.type) { case TreeLeafType::ROUND: leafProps.round.blockID = typeProps.round.blockID; leafProps.round.vRadius = AGE_LERP_UI16(typeProps.round.vRadius); leafProps.round.hRadius = AGE_LERP_UI16(typeProps.round.hRadius); break; case TreeLeafType::PINE: leafProps.pine.blockID = typeProps.pine.blockID; leafProps.pine.oRadius = AGE_LERP_UI16(typeProps.pine.oRadius); leafProps.pine.iRadius = AGE_LERP_UI16(typeProps.pine.iRadius); leafProps.pine.period = AGE_LERP_UI16(typeProps.pine.period); break; case TreeLeafType::MUSHROOM: leafProps.mushroom.capBlockID = typeProps.mushroom.capBlockID; leafProps.mushroom.gillBlockID = typeProps.mushroom.gillBlockID; leafProps.mushroom.interp = typeProps.mushroom.interp; leafProps.mushroom.tvRadius = AGE_LERP_UI16(typeProps.mushroom.tvRadius); leafProps.mushroom.thRadius = AGE_LERP_UI16(typeProps.mushroom.thRadius); leafProps.mushroom.bvRadius = AGE_LERP_UI16(typeProps.mushroom.bvRadius); leafProps.mushroom.bhRadius = AGE_LERP_UI16(typeProps.mushroom.bhRadius); leafProps.mushroom.bLength = AGE_LERP_UI16(typeProps.mushroom.bLength); leafProps.mushroom.capWidth = AGE_LERP_UI16(typeProps.mushroom.capWidth); leafProps.mushroom.gillWidth = AGE_LERP_UI16(typeProps.mushroom.gillWidth); break; default: break; } } inline void setBranchProps(TreeBranchProperties& branchProps, const TreeTypeBranchProperties& typeProps, f32 age) { branchProps.coreBlockID = typeProps.coreBlockID; branchProps.barkBlockID = typeProps.barkBlockID; branchProps.barkWidth = AGE_LERP_UI16(typeProps.barkWidth); branchProps.coreWidth = AGE_LERP_UI16(typeProps.coreWidth); branchProps.angle = typeProps.angle; branchProps.subBranchAngle = typeProps.subBranchAngle; branchProps.widthFalloff = AGE_LERP_UI16(typeProps.widthFalloff); branchProps.branchChance = AGE_LERP_F32(typeProps.branchChance); branchProps.changeDirChance = AGE_LERP_F32(typeProps.changeDirChance); setLeafProps(branchProps.leafProps, typeProps.leafProps, age); setFruitProps(branchProps.fruitProps, typeProps.fruitProps, age); } void FloraGenerator::generateTreeProperties(const NTreeType* type, f32 age, OUT TreeData& tree) { tree.age = age; tree.height = AGE_LERP_UI16(type->height); tree.branchPoints = AGE_LERP_UI16(type->branchPoints); tree.branchStep = AGE_LERP_UI16(type->branchStep); tree.killMult = AGE_LERP_UI16(type->killMult); tree.infRadius = AGE_LERP_F32(type->infRadius); // Set branch volume properties tree.branchVolumes.resize(type->branchVolumes.size()); for (size_t i = 0; i < tree.branchVolumes.size(); ++i) { BranchVolumeProperties& bp = tree.branchVolumes[i]; const TreeTypeBranchVolumeProperties& tbp = type->branchVolumes[i]; bp.height = AGE_LERP_UI16(tbp.height); bp.hRadius = AGE_LERP_UI16(tbp.hRadius); bp.vRadius = AGE_LERP_UI16(tbp.vRadius); bp.points = AGE_LERP_UI16(tbp.points); } // Set trunk properties tree.trunkProps.resize(type->trunkProps.size()); // TODO(Ben): no rand!!! tree.currentDir = rand() & 3; // & 3 == % 4 for (size_t i = 0; i < tree.trunkProps.size(); ++i) { TreeTrunkProperties& tp = tree.trunkProps[i]; const TreeTypeTrunkProperties& ttp = type->trunkProps[i]; tp.loc = ttp.loc; tp.coreWidth = AGE_LERP_UI16(ttp.coreWidth); tp.barkWidth = AGE_LERP_UI16(ttp.barkWidth); tp.branchChance = AGE_LERP_F32(ttp.branchChance); tp.coreBlockID = ttp.coreBlockID; tp.barkBlockID = ttp.barkBlockID; tp.interp = ttp.interp; // TODO(Ben): no rand!!! Range slopeRange; slopeRange.min = rand() % (ttp.slope.min.max - ttp.slope.min.min + 1) + ttp.slope.min.min; slopeRange.max = rand() % (ttp.slope.max.max - ttp.slope.max.min + 1) + ttp.slope.max.min; tp.slope = AGE_LERP_I32(slopeRange); // TODO(Ben): NO RAND tp.changeDirChance = ((f32)rand() / RAND_MAX) * (ttp.changeDirChance.max - ttp.changeDirChance.min) + ttp.changeDirChance.min; setFruitProps(tp.fruitProps, ttp.fruitProps, age); setLeafProps(tp.leafProps, ttp.leafProps, age); setBranchProps(tp.branchProps, ttp.branchProps, age); } } void FloraGenerator::generateFloraProperties(const FloraType* type, f32 age, OUT FloraData& flora) { flora.block = type->block; flora.slope = AGE_LERP_UI16(type->slope); flora.dSlope = AGE_LERP_UI16(type->dSlope); flora.height = AGE_LERP_UI16(type->height); flora.nextFlora = type->nextFlora; switch (type->dir) { case FloraDir::SIDE: flora.dir = rand() % 4; // TODO(Ben): Rand bad! break; case FloraDir::UP: flora.dir = TREE_UP; break; case FloraDir::DOWN: flora.dir = TREE_DOWN; break; } } #undef AGE_LERP_F32 #undef AGE_LERP_I32 #undef AGE_LERP_UI16 #ifdef VORB_OS_WINDOWS #pragma endregion #endif//VORB_OS_WINDOWS void FloraGenerator::spaceColonization(const f32v3& startPos) { std::vector attractPoints; // int numPoints = 500; f32 branchStep = (f32)m_treeData.branchStep; f32 infRadius = m_treeData.infRadius; f32 infRadius2 = infRadius * infRadius; f32 killRadius = (f32)(m_treeData.branchStep * m_treeData.killMult); f32 killRadius2 = killRadius * killRadius; for (auto& it : m_treeData.branchVolumes) { const f32v3 volCenter(startPos.x, startPos.y + it.height, startPos.z); const f32 hRadius = (f32)it.hRadius; const f32 hDiameter = hRadius * 2.0f; const f32 hRadius2 = hRadius * hRadius; const f32 vRadius = (f32)it.vRadius; const f32 vDiameter = vRadius * 2.0f; const f32 vRadius2 = vRadius * vRadius; // Generate attraction points for (int i = 0; i < it.points; i++) { // TODO(Ben): Worry about double and float casting f32v3 p(m_rGen.genlf() * hDiameter - hRadius, m_rGen.genlf() * vDiameter - vRadius, m_rGen.genlf() * hDiameter - hRadius); // Ellipsoid check f32 d = (p.x * p.x) / hRadius2 + (p.y * p.y) / vRadius2 + (p.z * p.z) / hRadius2; if (d <= 1.0f) { attractPoints.push_back(p + volCenter); } } } // Iteratively construct the tree int iter = 0; while (++iter < 10000) { if (attractPoints.size() < 5 || m_scNodes.empty()) return; for (int i = (int)attractPoints.size() - 1; i >= 0; --i) { f32 closestDist = FLT_MAX; int closestIndex = -1; // Get closest node and attract it towards attract point for (size_t j = 0; j < m_scNodes.size(); j++) { auto& tn = m_scNodes[j]; f32v3 v = attractPoints[i] - m_scRayNodes[tn.rayNode].pos; f32 dist2 = selfDot(v); if (dist2 <= killRadius2) { attractPoints[i] = attractPoints.back(); attractPoints.pop_back(); closestIndex = -1; break; } else if (dist2 <= infRadius2 && dist2 < closestDist) { closestDist = dist2; closestIndex = j; } } if (closestIndex != -1) { auto& tn = m_scNodes[closestIndex]; tn.dir += (attractPoints[i] - m_scRayNodes[tn.rayNode].pos) / closestDist; } } // Generate new nodes and erase unneeded ones for (int i = (int)m_scNodes.size() - 1; i >= 0; --i) { SCTreeNode& tn = m_scNodes.at(i); const SCRayNode& n = m_scRayNodes[tn.rayNode]; // Self dot? if (tn.dir.x && tn.dir.y && tn.dir.z) { f32v3 pos = n.pos + glm::normalize(tn.dir) * branchStep; tn.dir = f32v3(0.0f); ui32 nextIndex = m_scRayNodes.size(); // Change leaf node // TODO(Ben): This can be a vector mebby? auto it = m_scLeafSet.find(tn.rayNode); if (it != m_scLeafSet.end()) m_scLeafSet.erase(it); m_scLeafSet.insert(nextIndex); // Have to make temp copies with emplace_back ui16 trunkPropsIndex = n.trunkPropsIndex; m_scRayNodes.emplace_back(pos, tn.rayNode, trunkPropsIndex); m_scNodes.emplace_back(nextIndex); } else { // Remove it since its close to nothing // m_scNodes[i] = m_scNodes.back(); // m_scNodes.pop_back(); } } } } // Priority can not be bigger than 3 inline void FloraGenerator::tryPlaceNode(std::vector* nodes, ui8 priority, ui16 blockID, ui16 blockIndex, ui32 chunkOffset) { if (m_currChunkOff != chunkOffset) { m_currChunkOff = chunkOffset; auto it = m_nodeFieldsMap.find(chunkOffset); if (it == m_nodeFieldsMap.end()) { m_currNodeField = m_nodeFields.size(); m_nodeFields.emplace_back(); m_nodeFieldsMap.insert(std::pair(chunkOffset, m_currNodeField)); } else { m_currNodeField = it->second; } } // For memory compression we pack 4 nodes into each val NodeField& nf = m_nodeFields[m_currNodeField]; ui8& val = nf.vals[blockIndex >> 2]; ui8 shift = (blockIndex & 0x3) << 1; if ((((val >> shift) & 0x3) < priority)) { nodes->emplace_back(blockID, blockIndex, chunkOffset); // Overwrite priority val &= ~(0x3 << shift); val |= (priority << shift); } } // TODO(Ben): Need to handle different shapes than just round void FloraGenerator::makeTrunkSlice(ui32 chunkOffset, const TreeTrunkProperties& props) { // This function is so clever int width = (int)(props.coreWidth + props.barkWidth); if (width == 0) return; int innerWidth2 = (int)(props.coreWidth * props.coreWidth); int woodWidth2 = width * width; int woodWidth2m1 = (width - 1) * (width - 1); // Should get layer right outside outer layer int woodWidth2p1 = (width + 1) * (width + 1); // For leaves int leafWidth = 0; ui16 leafBlockID = 0; // Determine outer leaf shape switch (props.leafProps.type) { case TreeLeafType::ROUND: leafWidth = props.leafProps.round.hRadius; leafBlockID = props.leafProps.round.blockID; break; case TreeLeafType::PINE: if (props.leafProps.pine.period == 1) { leafWidth = props.leafProps.pine.oRadius; } else { leafWidth = fastFloor((1.0f - (f32)(m_h % props.leafProps.pine.period) / (props.leafProps.pine.period - 1)) * (props.leafProps.pine.oRadius - props.leafProps.pine.iRadius) + 0.5f) + props.leafProps.pine.iRadius; } leafBlockID = props.leafProps.pine.blockID; break; default: break; } width += leafWidth; int leafWidth2 = width * width; // Get position at back left corner int x = m_center.x; int z = m_center.z; width += 1; // Pad out one so we can check branches on small trees offsetNegative(x, z, X_1, Z_1, chunkOffset, width); x += width; z += width; // Y voxel offset int yOff = m_center.y * CHUNK_LAYER; // Distribute branch chance over the circumference of the core f64 branchChance = props.branchChance / (M_2_PI * (f64)(props.coreWidth + props.barkWidth)); for (int dz = -width; dz <= width; ++dz) { for (int dx = -width; dx <= width; ++dx) { int dist2 = dx * dx + dz * dz; if (dist2 < woodWidth2) { // Wood block // Get position i32v2 pos(x + dx, z + dz); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); if (dist2 > woodWidth2m1) { if (m_rGen.gen() % OUTER_SKIP_MOD) { if (dist2 < innerWidth2) { tryPlaceNode(m_wNodes, 3, props.coreBlockID, blockIndex, chunkOff); } else { tryPlaceNode(m_wNodes, 3, props.barkBlockID, blockIndex, chunkOff); } } } else { if (dist2 < innerWidth2) { tryPlaceNode(m_wNodes, 3, props.coreBlockID, blockIndex, chunkOff); } else { tryPlaceNode(m_wNodes, 3, props.barkBlockID, blockIndex, chunkOff); } } } else if (dist2 < woodWidth2p1) { // Fruit and branching // Get position i32v2 pos(x + dx, z + dz); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); if (m_rGen.genlf() < branchChance) { // TODO(Ben): If check if (!m_hasStoredTrunkProps) { m_scTrunkProps.push_back(props); m_hasStoredTrunkProps = true; } m_branchesToGenerate.emplace_back(blockIndex, chunkOff, dx, dz, m_scTrunkProps.size() - 1); } else if (leafWidth && leafBlockID) { tryPlaceNode(m_fNodes, 1, leafBlockID, blockIndex, chunkOff); } } else if (dist2 < leafWidth2 && leafBlockID) { // Leaves // Get position i32v2 pos(x + dx, z + dz); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); tryPlaceNode(m_fNodes, 1, leafBlockID, blockIndex, chunkOff); } } } } inline f32 fastFloorf(f32 x) { return FastConversion::floor(x); } inline f32 fastCeilf(f32 x) { return FastConversion::ceiling(x); } void FloraGenerator::generateBranch(ui32 chunkOffset, int x, int y, int z, f32 length, f32 width, f32 endWidth, f32v3 dir, bool makeLeaves, bool hasParent, const TreeBranchProperties& props) { if (width < 1.0f) return; int startX = x; int startY = y; int startZ = z; ui32 startChunkOffset = chunkOffset; f32v3 start(0.0f); f32v3 end = dir * length; // Compute bounding box f32 minX = 0.0f, maxX = 0.0f; f32 minY = 0.0f, maxY = 0.0f; f32 minZ = 0.0f, maxZ = 0.0f; if (end.x < minX) minX = end.x; if (end.x > maxX) maxX = end.x; if (end.y < minY) minY = end.y; if (end.y > maxY) maxY = end.y; if (end.z < minZ) minZ = end.z; if (end.z > maxZ) maxZ = end.z; // Pad the box by width + 1 f32 wp1 = glm::max(width, endWidth) + 1; minX -= wp1; maxX += wp1; minY -= wp1; maxY += wp1; minZ -= wp1; maxZ += wp1; // Round down to voxel position i32v3 min(fastFloor(minX), fastFloor(minY), fastFloor(minZ)); i32v3 max(fastFloor(maxX), fastFloor(maxY), fastFloor(maxZ)); // Offset to back corner offsetByPos(x, y, z, chunkOffset, min); // Make start and end relative to min start -= min; end -= min; const f32v3 ray = (end - start); const f32 l2 = selfDot(ray); // Iterate the volume and check points against line segment for (int i = 0; i < max.y - min.y; i++) { for (int j = 0; j < max.z - min.z; j++) { for (int k = 0; k < max.x - min.x; k++) { // http://stackoverflow.com/a/1501725/3346893 const f32v3 vec(k, i, j); const f32v3 v2 = vec - start; const f32 t = glm::dot(v2, ray) / l2; // Compute distance2 f32 dist2; f32 innerWidth; bool canFruit = false; if (t < 0.0) { dist2 = selfDot(v2); innerWidth = width; } else if (t > 1.0) { // Parent will fill these nodes in. if (hasParent) continue; dist2 = selfDot(vec - end); innerWidth = endWidth; } else { const f32v3 projection = start + t * ray; dist2 = selfDot(vec - projection); // Lerp the branch width innerWidth = lerp(width, endWidth, t); canFruit = true; } f32 width2 = innerWidth * innerWidth; f32 width2p1 = (innerWidth + 1) * (innerWidth + 1); f32 width2m1 = (innerWidth - 1) * (innerWidth - 1); if (width2m1 == 0) width2m1 = FLT_MAX; if (dist2 < width2) { i32v3 pos(x + k, y + i, z + j); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 newIndex = (ui16)(pos.x + pos.y * CHUNK_LAYER + pos.z * CHUNK_WIDTH); if (dist2 > width2m1 + 1) { if (m_rGen.gen() % OUTER_SKIP_MOD) tryPlaceNode(m_wNodes, 3, props.coreBlockID, newIndex, chunkOff); } else { tryPlaceNode(m_wNodes, 3, props.coreBlockID, newIndex, chunkOff); } } else if (canFruit && dist2 < width2p1 && props.fruitProps.flora != FLORA_ID_NONE) { // Distribute fruit chance over the circumference of the branch f64 fruitChance = props.fruitProps.chance / (M_2_PI * (f64)(innerWidth)); if (m_rGen.genlf() <= fruitChance) { i32v3 pos(x + k, y + i, z + j); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 newIndex = (ui16)(pos.x + pos.y * CHUNK_LAYER + pos.z * CHUNK_WIDTH); generateFlora(&m_genData->flora.at(props.fruitProps.flora), 1.0f, *m_fNodes, *m_wNodes, chunkOff, newIndex); } } } } } if (makeLeaves) { ui16 newIndex = (ui16)(startX + startY * CHUNK_LAYER + startZ * CHUNK_WIDTH); m_leavesToPlace.emplace_back(newIndex, startChunkOffset, &props.leafProps); } } void FloraGenerator::generateSCBranches() { // Check for performance issues. if (m_scRayNodes.size() > 4000) { if (m_scRayNodes.size() > 32768) { printf("ERROR: Tree has %zuray nodes but limited to 32768\n", m_scRayNodes.size()); m_scRayNodes.clear(); } else { printf("Performance warning: tree has %zu ray nodes\n", m_scRayNodes.size()); } } std::vector lNodesToAdd; // Set widths and sub branches for (auto& l : m_scLeafSet) { ui32 i = l; while (true) { SCRayNode& a = m_scRayNodes[i]; a.wasVisited = 1; // a helpful flag i = a.parent; if (i == SC_NO_PARENT) break; SCRayNode& b = m_scRayNodes[i]; TreeBranchProperties& tbp = m_scTrunkProps[b.trunkPropsIndex].branchProps; f32 nw = a.width + tbp.widthFalloff * m_treeData.branchStep; if (b.wasVisited) { if (b.width >= nw) break; b.width = nw; if (b.width > tbp.barkWidth + tbp.coreWidth) { b.width = (f32)(tbp.barkWidth + tbp.coreWidth); } } else { b.width = nw; if (b.width > tbp.barkWidth + tbp.coreWidth) { b.width = (f32)(tbp.barkWidth + tbp.coreWidth); } // Calculate sub branching if (tbp.branchChance > 0.0f) { f32v3 dir = glm::normalize(b.pos - a.pos); f32 length = glm::length(dir); dir /= length; for (f32 v = 0.0f; v < length; v += 1.0f) { if (m_rGen.genlf() < tbp.branchChance) { f32 width = lerp(a.width, b.width, v / length); // TODO(Ben): Width falloff aint working right? f32 sLength = width / tbp.widthFalloff; // Determine float position f32v3 pos = a.pos + dir * v; // Add root ui16 parent = m_scRayNodes.size(); ui16 tpIndex = a.trunkPropsIndex; // a and b are invalidated with emplace_back m_scRayNodes.emplace_back(pos, SC_NO_PARENT, tpIndex); m_scRayNodes.back().width = width; // Determine chain length int numSegments = (int)(sLength * tbp.changeDirChance); numSegments++; sLength /= numSegments; // Create branch chain int j = 0; f32v3 nDir = dir; while (true) { // Get new dir newDirFromAngle(nDir, tbp.subBranchAngle.min, tbp.subBranchAngle.max); pos += nDir * sLength; m_scRayNodes.emplace_back(pos, parent, tpIndex); m_scRayNodes.back().width = width * ((f32)(numSegments - j - 1) / numSegments) + 1.0f; // Check end condition if (++j == numSegments) break; parent = m_scRayNodes.size() - 1; if (parent >= 32767) { break; } } // Last node is leaf lNodesToAdd.push_back(m_scRayNodes.size() - 1); } } } } } } for (auto& i : lNodesToAdd) { m_scLeafSet.insert(i); } // Make branches // int a = 0; for (auto& l : m_scLeafSet) { ui32 i = l; bool hasLeaves = true; while (true) { SCRayNode& a = m_scRayNodes[i]; i = a.parent; if (i == SC_NO_PARENT) break; a.parent = SC_NO_PARENT; SCRayNode& b = m_scRayNodes[i]; f32v3 dir = b.pos - a.pos; f32 length = glm::length(dir); dir /= length; i32v3 iPosA(a.pos); int x = 0; int y = 0; int z = 0; ui32 chunkOffset = NO_CHUNK_OFFSET; offsetByPos(x, y, z, chunkOffset, iPosA); generateBranch(chunkOffset, x, y, z, length, a.width, b.width, dir, hasLeaves, b.parent != SC_NO_PARENT, m_scTrunkProps[b.trunkPropsIndex].branchProps); hasLeaves = false; } } } inline void FloraGenerator::generateLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props) { // TODO(Ben): OTHER TYPES switch (props.type) { case TreeLeafType::ROUND: if (props.round.vRadius == props.round.hRadius) { generateRoundLeaves(chunkOffset, x, y, z, props); } else { generateEllipseLeaves(chunkOffset, x, y, z, props); } break; case TreeLeafType::MUSHROOM: generateMushroomCap(chunkOffset, x, y, z, props); break; default: // Pine leaves cant generate on branches break; } } // Faster than ellipse for when the leaves are perfectly round void FloraGenerator::generateRoundLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props) { int radius = (int)(props.round.hRadius); int radius2 = radius * radius; int radius2m1 = (radius - 1) * (radius - 1); if (radius2m1 == 0) radius2m1 = INT_MAX; // Get position at back left corner offsetNegative(x, y, z, chunkOffset, radius - 1); x += radius; y += radius; z += radius; for (int dy = -radius; dy <= radius; ++dy) { for (int dz = -radius; dz <= radius; ++dz) { for (int dx = -radius; dx <= radius; ++dx) { int dist2 = dx * dx + dy * dy + dz * dz; if (dist2 < radius2) { i32v3 pos(x + dx, y + dy, z + dz); ui32 chunkOff = chunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + pos.y * CHUNK_LAYER + pos.z * CHUNK_WIDTH); if (dist2 > radius2m1) { if (m_rGen.gen() % OUTER_SKIP_MOD) tryPlaceNode(m_fNodes, 1, props.round.blockID, blockIndex, chunkOff); } else { tryPlaceNode(m_fNodes, 1, props.round.blockID, blockIndex, chunkOff); } } } } } } void FloraGenerator::generateEllipseLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props) { // Offset to bottom int offset = props.round.vRadius; // int tmp = y; offsetNegative(y, Y_1, chunkOffset, offset); // Use equation of ellipse f32 fOffset = (f32)offset; f32 a = (f32)props.round.hRadius; f32 b = (f32)(props.round.vRadius * props.round.vRadius); for (f32 i = 0.0f; i < fOffset * 2.0f + 1.0f; i += 1.0f) { f32 ey = fOffset - i; f32 radius = sqrtf(1.0f - (ey * ey) / b) * a; f32 radius2 = radius * radius; f32 radius2m1 = (radius - 1) * (radius - 1); if (radius2m1 == 0) radius2m1 = FLT_MAX; const int yOff = y * CHUNK_LAYER; // Offset to back left int offset = fastFloor(radius); int x2 = x; int z2 = z; ui32 innerChunkOffset = chunkOffset; offsetNegative(x2, z2, X_1, Z_1, innerChunkOffset, offset); x2 += offset; z2 += offset; // Make the layer for (int dz = -offset; dz <= offset; ++dz) { for (int dx = -offset; dx <= offset; ++dx) { int dist2 = dx * dx + dz * dz; if ((f32)dist2 < radius2) { i32v2 pos(x2 + dx, z2 + dz); ui32 chunkOff = innerChunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); if (dist2 > radius2m1) { if (m_rGen.gen() % OUTER_SKIP_MOD) tryPlaceNode(m_fNodes, 1, props.round.blockID, blockIndex, chunkOff); } else { tryPlaceNode(m_fNodes, 1, props.round.blockID, blockIndex, chunkOff); } } } } offsetPositive(y, Y_1, chunkOffset, 1); } } void FloraGenerator::generateMushroomCap(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props) { int startY = y; ui32 startChunkOffset = chunkOffset; if (props.mushroom.tvRadius) { // Top half // Offset to middle of cap int offset = props.mushroom.tvRadius; offsetNegative(y, Y_1, chunkOffset, offset); // Parameters f32 fOffset = (f32)offset; f32 a = (f32)props.mushroom.thRadius; f32 b = (f32)(props.mushroom.tvRadius * props.mushroom.tvRadius); f32 thickness = (f32)(props.mushroom.capWidth + props.mushroom.gillWidth); // Top half for (f32 i = 0.0f; i < fOffset + 1.0f; i += 1.0f) { // Equation of ellipse f32 radius = sqrtf(1.0f - (i * i) / b) * a; f32 capRadius2 = radius - props.mushroom.capWidth; capRadius2 = capRadius2 * capRadius2; f32 innerRadius2 = radius - thickness; if (innerRadius2 < 0.0f) { innerRadius2 = 0.0f; } else { innerRadius2 = innerRadius2 * innerRadius2; } f32 radius2 = radius * radius; const int yOff = y * CHUNK_LAYER; // Offset to back left int innerOffset = fastFloor(radius); int x2 = x; int z2 = z; ui32 innerChunkOffset = chunkOffset; offsetNegative(x2, z2, X_1, Z_1, innerChunkOffset, innerOffset); x2 += innerOffset; z2 += innerOffset; // Make the layer for (int dz = -innerOffset; dz <= innerOffset; ++dz) { for (int dx = -innerOffset; dx <= innerOffset; ++dx) { f32 dist2 = (f32)(dx * dx + dz * dz); if (dist2 < radius2 && dist2 > innerRadius2) { i32v2 pos(x2 + dx, z2 + dz); ui32 chunkOff = innerChunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); if (dist2 >= capRadius2) { tryPlaceNode(m_fNodes, 1, props.mushroom.capBlockID, blockIndex, chunkOff); } else { tryPlaceNode(m_fNodes, 1, props.mushroom.gillBlockID, blockIndex, chunkOff); } } } } offsetPositive(y, Y_1, chunkOffset, 1); } } if (props.mushroom.bLength && props.mushroom.bvRadius) { // Bottom half y = startY; chunkOffset = startChunkOffset; // Offset to bottom of cap and skip layers as needed int skipped = props.mushroom.bvRadius - props.mushroom.bLength; if (skipped < 0) return; int offset = props.mushroom.bvRadius - skipped; offsetNegative(y, Y_1, chunkOffset, offset + props.mushroom.tvRadius + 1); // Parameters f32 fOffset = (f32)props.mushroom.bvRadius; // f32 a = (f32)props.mushroom.bhRadius; f32 b = (f32)(props.mushroom.bvRadius * props.mushroom.bvRadius); f32 thickness = (f32)(props.mushroom.capWidth + props.mushroom.gillWidth); // Top half f32 end = fOffset + 1.0f; f32 fSkipped = (f32)skipped; for (f32 i = fSkipped; i < end; i += 1.0f) { // Get lerp factor f32 l = (i - fSkipped) / (end - fSkipped); smoothInterpFactor(l, props.mushroom.interp); // Lerp the a f32 a = ((f32)props.mushroom.thRadius - (f32)props.mushroom.bhRadius) * l + props.mushroom.bhRadius; // Equation of ellipse f32 ey = fOffset - i; f32 radius = sqrtf(1.0f - (ey * ey) / b) * a; f32 capRadius2 = radius - props.mushroom.capWidth; capRadius2 = capRadius2 * capRadius2; f32 innerRadius2 = radius - thickness; if (innerRadius2 < 0.0f) { innerRadius2 = 0.0f; } else { innerRadius2 = innerRadius2 * innerRadius2; } f32 radius2 = radius * radius; const int yOff = y * CHUNK_LAYER; // Offset to back left int innerOffset = fastFloor(radius); int x2 = x; int z2 = z; ui32 innerChunkOffset = chunkOffset; offsetNegative(x2, z2, X_1, Z_1, innerChunkOffset, innerOffset); x2 += innerOffset; z2 += innerOffset; // Make the layer for (int dz = -innerOffset; dz <= innerOffset; ++dz) { for (int dx = -innerOffset; dx <= innerOffset; ++dx) { f32 dist2 = (f32)(dx * dx + dz * dz); if (dist2 < radius2 && dist2 > innerRadius2) { i32v2 pos(x2 + dx, z2 + dz); ui32 chunkOff = innerChunkOffset; addChunkOffset(pos, chunkOff); ui16 blockIndex = (ui16)(pos.x + yOff + pos.y * CHUNK_WIDTH); if (dist2 >= capRadius2) { tryPlaceNode(m_fNodes, 1, props.mushroom.capBlockID, blockIndex, chunkOff); } else { tryPlaceNode(m_fNodes, 1, props.mushroom.gillBlockID, blockIndex, chunkOff); } } } } offsetPositive(y, Y_1, chunkOffset, 1); } } } inline void FloraGenerator::newDirFromAngle(f32v3& dir, f32 minAngle, f32 maxAngle) { f32 angle = (f32)(m_rGen.genlf() * (maxAngle - minAngle) + minAngle); f32v3 relDir(0.0f, cos(angle), sin(angle)); relDir = glm::angleAxis((f32)(m_rGen.genlf() * 360.0), f32v3(0.0f, 1.0f, 0.0f)) * relDir; // Transform relDir relative to dir with change of basis matrix f32v3 nz = glm::cross(dir, f32v3(0.0f, 1.0f, 0.0f)); f32v3 nx = glm::cross(dir, nz); f32m3 trans(nx, dir, nz); dir = trans * relDir; } ================================================ FILE: SoA/FloraGenerator.h ================================================ /// /// NFloraGenerator.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 15 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Generates flora and trees /// #pragma once #ifndef NFloraGenerator_h__ #define NFloraGenerator_h__ #include "Flora.h" #include "Chunk.h" #include "soaUtils.h" // 0111111111 0111111111 0111111111 = 0x1FF7FDFF #define NO_CHUNK_OFFSET 0x1FF7FDFF struct PlanetGenData; // Will not work for chunks > 32^3 struct FloraNode { FloraNode(ui16 blockID, ui16 blockIndex, ui32 chunkOffset) : blockID(blockID), blockIndex(blockIndex), chunkOffset(chunkOffset) { }; ui16 blockID; ui16 blockIndex; // TODO(Ben): ui32 instead for massive trees? Use leftover bits for Y? ui32 chunkOffset; ///< Packed 00 XXXXXXXXXX YYYYYYYYYY ZZZZZZZZZZ for positional offset. 00111 == 0 }; #define SC_NO_PARENT 0x7FFFu struct SCRayNode { SCRayNode(const f32v3& pos, ui16 parent, ui16 trunkPropsIndex) : pos(pos), trunkPropsIndex(trunkPropsIndex), wasVisited(0), parent(parent){}; f32v3 pos; ui16 trunkPropsIndex; ui16 wasVisited; ui16 parent; f32 width = 1.0f; }; struct SCTreeNode { SCTreeNode(ui16 rayNode) : rayNode(rayNode), dir(0.0f) {}; ui16 rayNode; f32v3 dir; }; struct NodeField { NodeField() { memset(vals, 0, sizeof(vals)); } // Each node is 2 bits ui8 vals[CHUNK_SIZE / 4]; }; // Defer leaf placement to prevent node overlap struct LeavesToPlace { LeavesToPlace(ui16 blockIndex, ui32 chunkOffset, const TreeLeafProperties* leafProps) : blockIndex(blockIndex), chunkOffset(chunkOffset), leafProps(leafProps) { } ui16 blockIndex; ui32 chunkOffset; const TreeLeafProperties* leafProps; }; // Defer manual branch placement so it doesn't conflict with SC struct BranchToGenerate { BranchToGenerate(ui16 blockIndex, ui32 chunkOffset, i32 dx, i32 dz, ui16 trunkPropsIndex) : blockIndex(blockIndex), trunkPropsIndex(trunkPropsIndex), chunkOffset(chunkOffset), dx(dx), dz(dz) { } ui16 blockIndex; ui16 trunkPropsIndex; ui32 chunkOffset; i32 dx; i32 dz; }; // TODO(Ben): Add comments class FloraGenerator { public: /// @brief Generates flora for a chunk using its QueuedFlora. /// @param chunk: Chunk who's flora should be generated. /// @param gridData: The heightmap to use /// @param fNodes: Returned low priority nodes, for flora and leaves. /// @param wNodes: Returned high priority nodes, for tree "wood". void generateChunkFlora(const Chunk* chunk, const PlanetHeightData* heightData, OUT std::vector& fNodes, OUT std::vector& wNodes); /// Generates standalone tree. void generateTree(const NTreeType* type, f32 age, OUT std::vector& fNodes, OUT std::vector& wNodes, const PlanetGenData* genData, ui32 chunkOffset = NO_CHUNK_OFFSET, ui16 blockIndex = 0); /// Generates standalone flora. void generateFlora(const FloraType* type, f32 age, OUT std::vector& fNodes, OUT std::vector& wNodes, ui32 chunkOffset = NO_CHUNK_OFFSET, ui16 blockIndex = 0); /// Generates a specific tree's properties static void generateTreeProperties(const NTreeType* type, f32 age, OUT TreeData& tree); static void generateFloraProperties(const FloraType* type, f32 age, OUT FloraData& flora); void spaceColonization(const f32v3& startPos); static inline int getChunkXOffset(ui32 chunkOffset) { return (int)((chunkOffset >> 20) & 0x3FF) - 0x1FF; } static inline int getChunkYOffset(ui32 chunkOffset) { return (int)((chunkOffset >> 10) & 0x3FF) - 0x1FF; } static inline int getChunkZOffset(ui32 chunkOffset) { return (int)(chunkOffset & 0x3FF) - 0x1FF; } private: enum TreeDir { TREE_LEFT = 0, TREE_BACK, TREE_RIGHT, TREE_FRONT, TREE_UP, TREE_DOWN, TREE_NO_DIR }; void tryPlaceNode(std::vector* nodes, ui8 priority, ui16 blockID, ui16 blockIndex, ui32 chunkOffset); void makeTrunkSlice(ui32 chunkOffset, const TreeTrunkProperties& props); void generateBranch(ui32 chunkOffset, int x, int y, int z, f32 length, f32 width, f32 endWidth, f32v3 dir, bool makeLeaves, bool hasParent, const TreeBranchProperties& props); void generateSCBranches(); void generateLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props); void generateRoundLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props); void generateEllipseLeaves(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props); void generateMushroomCap(ui32 chunkOffset, int x, int y, int z, const TreeLeafProperties& props); void newDirFromAngle(f32v3& dir, f32 minAngle, f32 maxAngle); std::set m_scLeafSet; std::unordered_map m_nodeFieldsMap; std::vector m_nodeFields; std::vector m_scRayNodes; std::vector m_scNodes; std::vector m_leavesToPlace; std::vector m_branchesToGenerate; std::vector m_scTrunkProps; ///< Stores branch properties for nodes std::vector* m_fNodes; std::vector* m_wNodes; TreeData m_treeData; // FloraData m_floraData; i32v3 m_center; ui32 m_h; ///< Current height along the tree FastRandGenerator m_rGen; ui32 m_currChunkOff; ui32 m_currNodeField; const PlanetGenData* m_genData; bool m_hasStoredTrunkProps; }; #endif // NFloraGenerator_h__ ================================================ FILE: SoA/FragFile.cpp ================================================ #include "stdafx.h" #include "FragFile.h" // Fragmentation Seeking Information class FragBlockHeader { public: // Path ID i32 id; // The Size Of The Current Block i32 sizeBlock; // Offset Into The File Relative To The End Of Data Block i32 offset; }; // A Header For Each Data Path class FragHeader { public: // The Total Size Of The Path i32 totalSize; // First Data Block FragBlockHeader firstBlock; // Last Data Block FragBlockHeader lastBlock; }; FragFile::FragFile(i32 numPaths, const cString path, bool isReadonly) : _file(nullptr), _curPath(0), _numDataPaths(numPaths), _headerSizeInBytes(_numDataPaths * sizeof(FragHeader)), _headers(nullptr) { bool isSuccess = isReadonly ? openReadonlyFile(path) : openWritingFile(path); if (!isSuccess) { _file = nullptr; #ifdef DEBUG printf("Error Attempting To Open Fragmented File:\n%s\n", path); #endif // DEBUG } } FragFile::~FragFile() { close(); } void FragFile::setDataPath(i32 p) { if (p < 0 || p >= _numDataPaths) { #ifdef DEBUG printf("Attempting To Access Invalid Path\n"); #endif // DEBUG exit(-1); } _curPath = p; } i32 FragFile::getDataPathSize() const { return _headers[_curPath].totalSize; } void FragFile::read(void* buf) { ui8* data = (ui8*)buf; // Get Data Block Information i32 bytesLeft = getDataPathSize(); FragBlockHeader header = _headers[_curPath].firstBlock; // Move To The First Block fseek(_file, header.offset + _headerSizeInBytes, SEEK_SET); while (bytesLeft > 0) { // Read The Header For The Block fread(&header, sizeof(FragBlockHeader), 1, _file); // Read The Data Of The Current Block fread(data, 1, header.sizeBlock, _file); bytesLeft -= header.sizeBlock; // Move Pointers data += header.sizeBlock; // Seek The Next Block if (bytesLeft > 0) fseek(_file, header.offset, SEEK_CUR); } } void FragFile::append(void* data, i32 sizeInBytes) { // Move To The End Of The File fseek(_file, 0, SEEK_END); i32 initialSize = getDataPathSize(); _headers[_curPath].totalSize += sizeInBytes; // Create The Header FragBlockHeader header = {}; header.id = _curPath; header.sizeBlock = sizeInBytes; // Store Our Position i32 fpos = ftell(_file); // Write The Fragmented Data Block fwrite(&header, sizeof(FragBlockHeader), 1, _file); fwrite(data, 1, sizeInBytes, _file); // Allow Seek Time fseek(_file, _curPath * sizeof(FragHeader), SEEK_SET); // Exchange Info About Last Known Block i32 otherPos = _headers[_curPath].lastBlock.offset; _headers[_curPath].lastBlock.offset = fpos - _headerSizeInBytes; i32 otherSize = _headers[_curPath].lastBlock.sizeBlock; _headers[_curPath].lastBlock.sizeBlock = sizeInBytes; // Check If We've Written Anything Before if (initialSize == 0) { _headers[_curPath].firstBlock = _headers[_curPath].lastBlock; } // Write The New Header fwrite(&_headers[_curPath], sizeof(FragHeader), 1, _file); if (initialSize != 0) { // Go To The Previous Last Block fseek(_file, otherPos + _headerSizeInBytes, SEEK_SET); // Reset Offset Information header.offset = fpos - otherPos; header.offset -= otherSize + sizeof(FragBlockHeader); header.offset -= _headerSizeInBytes; // Overwrite header.sizeBlock = otherSize; fwrite(&header, sizeof(FragBlockHeader), 1, _file); } } void FragFile::overwrite(void* data VORB_UNUSED, i32 fileDataOffset VORB_UNUSED) { // TODO: Implement } void FragFile::defragment(const cString tmpFileName VORB_UNUSED) { // TODO: Implement } void FragFile::flush() { fflush(_file); } void FragFile::close() { if (_file) { flush(); fclose(_file); _file = nullptr; } if (_headers) { delete[] _headers; _headers = nullptr; } } bool FragFile::openReadonlyFile(const cString path) { // Check For A Path if (!path) return false; // Attempt To Open The File _file = fopen(path, "rb"); if (_file == nullptr) return false; // Read The Headers (They Must Be There) fseek(_file, 0, SEEK_END); i32 len = ftell(_file); fseek(_file, 0, SEEK_SET); len -= ftell(_file); if (len < _headerSizeInBytes) { // The Headers Must Have Not Been Written fclose(_file); return false; } else { // Read The Headers _headers = new FragHeader[_numDataPaths]; fread(_headers, sizeof(FragHeader), _numDataPaths, _file); return true; } } bool FragFile::openWritingFile(const cString path) { // Check For A Path if (!path) return false; // Attempt To Open The File _file = fopen(path, "wb+"); if (_file == nullptr) return false; // Try To Read The Headers fseek(_file, 0, SEEK_END); i32 len = ftell(_file); fseek(_file, 0, SEEK_SET); len -= ftell(_file); if (len < _headerSizeInBytes) { // Write New Headers _headers = new FragHeader[_numDataPaths](); for (i32 i = 0; i < _numDataPaths; i++) { _headers[i].firstBlock.id = i; _headers[i].firstBlock.id = i; } fwrite(_headers, sizeof(FragHeader), _numDataPaths, _file); } else { // Read The Headers _headers = new FragHeader[_numDataPaths]; fread(_headers, sizeof(FragHeader), _numDataPaths, _file); } return true; } ================================================ FILE: SoA/FragFile.h ================================================ #pragma once #include "Vorb/types.h" // Header For Each Data Path That Allows Fragmented Functionality class FragHeader; // A File That Stores Multiple Data Paths In Fragmented Blocks class FragFile { public: FragFile(i32 numPaths, const cString path, bool isReadonly); ~FragFile(); // Point To A Data Path void setDataPath(i32 p); // Total Number Of Data Paths Found In The File i32 getNumDataPaths() const { return _numDataPaths; } // Size In Bytes Of The Data Path i32 getDataPathSize() const; // Reads An Entire Data Path Into A Buffer void read(void* buf); // Appends Data Into The Current Data Path void append(void* data, i32 sizeInBytes); // Overwrites/Appends Data From An Offset Into The Current Data Path void overwrite(void* data, i32 fileDataOffset); // Defragment The File By Using A Temporary File Name (Will Perform A Swap/Delete On The Temp File) void defragment(const cString tmpFileName); // OS File Operations void flush(); void close(); private: // IO Helpers bool openReadonlyFile(const cString path); bool openWritingFile(const cString path); // The Actual File FILE* _file; // Which Data Path To Read From i32 _curPath; // Data Path Information const i32 _numDataPaths; const i32 _headerSizeInBytes; FragHeader* _headers; }; ================================================ FILE: SoA/FreeMoveComponentUpdater.cpp ================================================ #include "stdafx.h" #include "FreeMoveComponentUpdater.h" #include "SpaceSystem.h" #include "GameSystem.h" #include "Constants.h" void FreeMoveComponentUpdater::update(GameSystem* gameSystem, SpaceSystem* spaceSystem) { //TODO(Ben): A lot of temporary code here f64v3 forward, right, up; for (auto& it : gameSystem->freeMoveInput) { auto& fmcmp = it.second; auto& physcmp = gameSystem->physics.get(fmcmp.physicsComponent); f64q* orientation; f64 acceleration = (f64)fmcmp.speed * 0.01; // If there is a voxel component, we use voxel position if (physcmp.voxelPosition) { // No acceleration on voxels physcmp.velocity = f64v3(0.0); acceleration = 1.0; auto& vpCmp = gameSystem->voxelPosition.get(physcmp.voxelPosition); //f64 radius = spaceSystem->sphericalGravity.get(vpCmp.parentVoxel).radius; orientation = &vpCmp.orientation; if (fmcmp.superSpeed) { static const f64 SS_Mult = 0.01; acceleration *= glm::min(600.0, glm::max(2.0, (SS_Mult * vpCmp.gridPosition.pos.y))); // temporary } } else { auto& spCmp = gameSystem->spacePosition.get(physcmp.spacePosition); f64 radius = spaceSystem->sphericalGravity.get(spCmp.parentGravity).radius; orientation = &spCmp.orientation; acceleration *= KM_PER_VOXEL; if (fmcmp.superSpeed) { static const f64 SS_Mult = 0.1; acceleration *= glm::max(2.0, (SS_Mult * (glm::length(spCmp.position) - radius))); // temporary, assumes a parent } } // Calculate velocity vector from inputs and speed if (fmcmp.tryMoveForward) { forward = *orientation * f64v3(0.0, 0.0, 1.0); physcmp.velocity += forward * acceleration; } else if (fmcmp.tryMoveBackward) { forward = *orientation * f64v3(0.0, 0.0, 1.0); physcmp.velocity -= forward * acceleration; } if (fmcmp.tryMoveRight) { right = *orientation * f64v3(-1.0, 0.0, 0.0); physcmp.velocity += right * acceleration; } else if (fmcmp.tryMoveLeft) { right = *orientation * f64v3(-1.0, 0.0, 0.0); physcmp.velocity -= right * acceleration; } if (fmcmp.tryMoveUp) { up = *orientation * f64v3(0.0, 1.0, 0.0); physcmp.velocity += up * acceleration; } else if (fmcmp.tryMoveDown) { up = *orientation * f64v3(0.0, 1.0, 0.0); physcmp.velocity -= up * acceleration; } #define ROLL_SPEED 0.7 if (fmcmp.tryRollLeft) { forward = *orientation * f64v3(0.0, 0.0, 1.0); *orientation = glm::angleAxis(-ROLL_SPEED, forward) * (*orientation); } else if (fmcmp.tryRollRight) { forward = *orientation * f64v3(0.0, 0.0, 1.0); *orientation = glm::angleAxis(ROLL_SPEED, forward) * (*orientation); } } } void FreeMoveComponentUpdater::rotateFromMouse(GameSystem* gameSystem, FreeMoveInputComponent& cmp, float dx, float dy, float speed) { auto& physcmp = gameSystem->physics.get(cmp.physicsComponent); f64q* orientation; // If there is a voxel component, we use voxel position if (physcmp.voxelPosition) { orientation = &gameSystem->voxelPosition.get(physcmp.voxelPosition).orientation; } else { orientation = &gameSystem->spacePosition.get(physcmp.spacePosition).orientation; } f64v3 right = *orientation * f64v3(1.0, 0.0, 0.0); f64v3 up = *orientation * f64v3(0.0, 1.0, 0.0); f64q upQuat = glm::angleAxis((f64)(dy * speed), right); f64q rightQuat = glm::angleAxis((f64)(dx * speed), up); *orientation = upQuat * rightQuat * (*orientation); } ================================================ FILE: SoA/FreeMoveComponentUpdater.h ================================================ /// /// FreeMoveComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates FreeMoveComponents /// #pragma once #ifndef FreeMoveComponentUpdater_h__ #define FreeMoveComponentUpdater_h__ class GameSystem; class SpaceSystem; struct FreeMoveInputComponent; class FreeMoveComponentUpdater { public: void update(GameSystem* gameSystem, SpaceSystem* spaceSystem); static void rotateFromMouse(GameSystem* gameSystem, FreeMoveInputComponent& cmp, float dx, float dy, float speed); }; #endif // FreeMoveComponentUpdater_h__ ================================================ FILE: SoA/Frustum.cpp ================================================ #include "stdafx.h" #include "Frustum.h" #include "Constants.h" void Frustum::Plane::setNormalAndPoint(const f32v3 &normal, const f32v3 &point) { this->normal = glm::normalize(normal); d = -(glm::dot(this->normal, point)); } void Frustum::Plane::setCoefficients(float a, float b, float c, float d) { //compute the length of the vector float l = glm::length(f32v3(a, b, c)); // normalize the vector normal = f32v3(a / l, b / l, c / l); // and divide d by th length as well this->d = d / l; } float Frustum::Plane::distance(const f32v3 &p) const { return (d + glm::dot(normal, p)); } void Frustum::setCamInternals(float fov, float aspectRatio, float znear, float zfar) { #define RADIANS_PER_DEGREE M_PI / 180.0f // store the information m_fov = fov; m_aspectRatio = aspectRatio; m_znear = znear; m_zfar = zfar; // compute width and height of the near and far plane sections float tang = (float)tan(RADIANS_PER_DEGREE * fov * 0.5f); m_nh = m_znear * tang; m_nw = m_nh * m_aspectRatio; m_fh = m_zfar * tang; m_fw = m_fh * m_aspectRatio; } void Frustum::updateFromWVP(const f32m4& WVP) { m_planes[NEARP].setCoefficients( WVP[0][2] + WVP[0][3], WVP[1][2] + WVP[1][3], WVP[2][2] + WVP[2][3], WVP[3][2] + WVP[3][3]); m_planes[FARP].setCoefficients( -WVP[0][2] + WVP[0][3], -WVP[1][2] + WVP[1][3], -WVP[2][2] + WVP[2][3], -WVP[3][2] + WVP[3][3]); m_planes[BOTTOMP].setCoefficients( WVP[0][1] + WVP[0][3], WVP[1][1] + WVP[1][3], WVP[2][1] + WVP[2][3], WVP[3][1] + WVP[3][3]); m_planes[TOPP].setCoefficients( -WVP[0][1] + WVP[0][3], -WVP[1][1] + WVP[1][3], -WVP[2][1] + WVP[2][3], -WVP[3][1] + WVP[3][3]); m_planes[LEFTP].setCoefficients( WVP[0][0] + WVP[0][3], WVP[1][0] + WVP[1][3], WVP[2][0] + WVP[2][3], WVP[3][0] + WVP[3][3]); m_planes[RIGHTP].setCoefficients( -WVP[0][0] + WVP[0][3], -WVP[1][0] + WVP[1][3], -WVP[2][0] + WVP[2][3], -WVP[3][0] + WVP[3][3]); } void Frustum::update(const f32v3& position, const f32v3& dir, const f32v3& up) { f32v3 nc, fc, X, Y, Z; // Compute the Z axis of camera // This axis points in the opposite direction from // the looking direction Z = glm::normalize(position - dir); // X axis of camera with given "up" vector and Z axis X = glm::normalize(glm::cross(up, Z)); // The real "up" vector is the cross product of Z and X Y = glm::cross(Z, X); // compute the centers of the near and far planes nc = position - Z * m_znear; fc = position - Z * m_zfar; m_planes[NEARP].setNormalAndPoint(-Z, nc); m_planes[FARP].setNormalAndPoint(Z, fc); f32v3 aux, normal; aux = glm::normalize((nc + Y * m_nh) - position); normal = glm::cross(aux, X); m_planes[TOPP].setNormalAndPoint(normal, nc + Y * m_nh); aux = glm::normalize((nc - Y * m_nh) - position); normal = glm::cross(X, aux); m_planes[BOTTOMP].setNormalAndPoint(normal, nc - Y * m_nh); aux = glm::normalize((nc - X * m_nw) - position); normal = glm::cross(aux, Y); m_planes[LEFTP].setNormalAndPoint(normal, nc - X * m_nw); aux = glm::normalize((nc + X * m_nw) - position); normal = glm::cross(Y, aux); m_planes[RIGHTP].setNormalAndPoint(normal, nc + X * m_nw); } bool Frustum::pointInFrustum(const f32v3& pos) const { for (int p = 0; p < 4; p++) { //*************************************** IGNORING FAR AND NEAR CLIPPING PLANE if (m_planes[p].distance(pos) <= 0) return false; } return true; } bool Frustum::sphereInFrustum(const f32v3& pos, float radius) const { for (int p = 0; p < 4; p++) { //*************************************** IGNORING FAR AND NEAR CLIPPING PLANE if (m_planes[p].distance(pos) <= -radius) return false; } return true; } ================================================ FILE: SoA/Frustum.h ================================================ /// /// Frustum.h /// Vorb Engine /// /// Created by Benjamin Arnold on 24 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// This file implements a frustum used for frustum culling /// #pragma once #ifndef Frustum_h__ #define Frustum_h__ #include "Vorb/types.h" class Frustum { public: enum Planes { RIGHTP = 0, LEFTP, BOTTOMP, TOPP, FARP, NEARP }; class Plane { public: void setNormalAndPoint(const f32v3 &normal, const f32v3 &point); void setCoefficients(float a, float b, float c, float d); float distance(const f32v3 &p) const; f32v3 normal; float d; }; /// Sets internal camera properties. Needed for update() void setCamInternals(float fov, float aspectRatio, float znear, float zfar); /// Updates the frustum with the projection and view matrix /// @param MVP: World-View-Projection matrix of camera void updateFromWVP(const f32m4& WVP); /// Updates the frustum with the geometric information void update(const f32v3& position, const f32v3& dir, const f32v3& up); /// Checks if a point is in the frustum /// @param pos: The position of the point /// @return true if it is in the frustum bool pointInFrustum(const f32v3& pos) const; /// Checks if a sphere is in the frustum /// @param pos: Center position of the sphere /// @param radius: Radius of the sphere /// @return true if it is in the frustum bool sphereInFrustum(const f32v3& pos, float radius) const; private: float m_fov = 0.0f; ///< Vertical field of view in degrees float m_aspectRatio = 0.0f; ///< Screen aspect ratio float m_znear = 0.0f; ///< Near clipping plane float m_zfar = 0.0f; ///< Far clipping plane float m_nh = 0.0f; ///< Near plane height float m_nw = 0.0f; ///< Near plane Width float m_fh = 0.0f; ///< Far plane height float m_fw = 0.0f; ///< Far plane Width Plane m_planes[6]; ///< The actual frustum data }; #endif // Frustum_h__ ================================================ FILE: SoA/FrustumComponentUpdater.cpp ================================================ #include "stdafx.h" #include "FrustumComponentUpdater.h" #include "GameSystem.h" void FrustumComponentUpdater::update(OUT GameSystem* gameSystem) { f64q orientation; f32v3 up; f32v3 dir; const f32v3 pos(0.0f); ///< Always treat as origin for precision for (auto& it : gameSystem->frustum) { auto& cmp = it.second; // Get orientation based on position and head if (cmp.voxelPosition) { auto& vpCmp = gameSystem->voxelPosition.get(cmp.voxelPosition); orientation = vpCmp.orientation; } else { auto& spCmp = gameSystem->spacePosition.get(cmp.spacePosition); orientation = spCmp.orientation; } if (cmp.head) { auto& hCmp = gameSystem->head.get(cmp.head); orientation = orientation * hCmp.relativeOrientation; } up = orientation * f64v3(0.0, 1.0, 0.0); dir = orientation * f64v3(0.0, 0.0, 1.0); cmp.frustum.update(pos, dir, up); } } ================================================ FILE: SoA/FrustumComponentUpdater.h ================================================ /// /// FrustumComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 25 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates frustum components /// #pragma once #ifndef FrustumComponentUpdater_h__ #define FrustumComponentUpdater_h__ #include "Vorb/decorators.h" class GameSystem; class FrustumComponentUpdater { public: /// Updates frustum components /// @param gameSystem: Game ECS void update(OUT GameSystem* gameSystem); }; #endif // FrustumComponentUpdater_h__ ================================================ FILE: SoA/GameManager.cpp ================================================ #include "stdafx.h" #include "GameManager.h" #ifdef VORB_OS_WINDOWS #include //for mkdir windows #endif//VORB_OS_WINDOWS #include #include #include #include "BlockData.h" #include "CAEngine.h" #include "Chunk.h" #include "ChunkIOManager.h" #include "DebugRenderer.h" #include "InputMapper.h" #include "Inputs.h" #include "SoaOptions.h" #include "VRayHelper.h" #include "WSO.h" #include "WSOAtlas.h" #include "WSOData.h" #include "WSOScanner.h" #include "VoxelEditor.h" bool GameManager::gameInitialized = false; bool GameManager::_systemsInitialized = false; float GameManager::fogStart, GameManager::fogEnd; VoxelEditor* GameManager::voxelEditor = nullptr; WSOAtlas* GameManager::wsoAtlas = nullptr; WSOScanner* GameManager::wsoScanner = nullptr; vg::TextureCache* GameManager::textureCache = nullptr; void GameManager::initializeSystems() { if (_systemsInitialized == false) { voxelEditor = new VoxelEditor(); wsoAtlas = new WSOAtlas(); wsoAtlas->load("Data\\WSO\\test.wso"); wsoScanner = new WSOScanner(wsoAtlas); textureCache = new vg::TextureCache(); _systemsInitialized = true; } } void GameManager::registerTexturesForLoad() { //texturePackLoader->registerTexture("FarTerrain/location_marker.png"); //texturePackLoader->registerTexture("FarTerrain/terrain_texture.png", &vg::SamplerState::LINEAR_WRAP_MIPMAP); //texturePackLoader->registerTexture("FarTerrain/normal_leaves_billboard.png"); //texturePackLoader->registerTexture("FarTerrain/pine_leaves_billboard.png"); //texturePackLoader->registerTexture("FarTerrain/mushroom_cap_billboard.png"); //texturePackLoader->registerTexture("FarTerrain/tree_trunk_1.png"); //texturePackLoader->registerTexture("Blocks/Liquids/water_normal_map.png", &vg::SamplerState::LINEAR_WRAP_MIPMAP); //texturePackLoader->registerTexture("Sky/StarSkybox/front.png"); //texturePackLoader->registerTexture("Sky/StarSkybox/right.png"); //texturePackLoader->registerTexture("Sky/StarSkybox/top.png"); //texturePackLoader->registerTexture("Sky/StarSkybox/left.png"); //texturePackLoader->registerTexture("Sky/StarSkybox/bottom.png"); //texturePackLoader->registerTexture("Sky/StarSkybox/back.png"); //texturePackLoader->registerTexture("FarTerrain/water_noise.png", &vg::SamplerState::LINEAR_WRAP_MIPMAP); //texturePackLoader->registerTexture("Particle/ball_mask.png"); //texturePackLoader->registerTexture("GUI/crosshair.png"); } void GameManager::getTextureHandles() { } void GameManager::saveState() { savePlayerState(); // saveOptions(); // voxelWorld->getChunkManager().saveAllChunks(); } void GameManager::savePlayerState() { // fileManager.savePlayerFile(player); } bool isSolidBlock(const i32& blockID VORB_UNUSED) { return true; // return blockID && (blockID < LOWWATER || blockID > FULLWATER); } void GameManager::clickDragRay(ChunkManager* chunkManager VORB_UNUSED, Player* player VORB_UNUSED, bool isBreakRay VORB_UNUSED) { #define MAX_RANGE 120.0f //VoxelRayQuery rq; //if (isBreakRay) { // // Obtain The Simple Query // rq = VRayHelper::getQuery(player->getChunkCamera().getPosition(), player->chunkDirection(), MAX_RANGE, chunkManager, isSolidBlock); // // Check If Something Was Picked // if (rq.distance > MAX_RANGE || rq.id == NONE) return; //} else { // // Obtain The Full Query // VoxelRayFullQuery rfq = VRayHelper::getFullQuery(player->getChunkCamera().getPosition(), player->chunkDirection(), MAX_RANGE, chunkManager, isSolidBlock); // // Check If Something Was Picked // if (rfq.inner.distance > MAX_RANGE || rfq.inner.id == NONE) return; // // We Want This Indexing Information From The Query // rq = rfq.outer; //} //if (rq.chunk == nullptr) { // return; //} //i32v3 position = rq.location; //if (voxelEditor->isEditing() == false) { // voxelEditor->setStartPosition(position); // voxelEditor->setEndPosition(position); //} else { // voxelEditor->setEndPosition(position); //} } void GameManager::scanWSO(ChunkManager* chunkManager VORB_UNUSED, Player* player VORB_UNUSED) { #define SCAN_MAX_DISTANCE 20.0 /* VoxelRayQuery rq = VRayHelper::getQuery( player->getChunkCamera().getPosition(), player->getChunkCamera().getDirection(), SCAN_MAX_DISTANCE, chunkManager, isSolidBlock ); if (rq.distance > SCAN_MAX_DISTANCE || rq.id == 0) return; auto wsos = wsoScanner->scanWSOs(rq.location, chunkManager); for (i32 i = 0; i < wsos.size(); i++) { f32v3 wsoPos(wsos[i]->position); f32v3 wsoSize(wsos[i]->data->size); wsoPos += wsoSize * 0.5f; debugRenderer->drawCube(wsoPos, wsoSize + 0.3f, f32v4(1, 1, 0, 0.1f), 2.0); delete wsos[i]; }*/ } void GameManager::onQuit() { // GLuint st = SDL_GetTicks(); saveState(); } void GameManager::endSession() { onQuit(); #ifdef _DEBUG //_CrtDumpMemoryLeaks(); #endif } ================================================ FILE: SoA/GameManager.h ================================================ #pragma once #include #include #include #include "WorldStructs.h" class ChunkSlot; class Player; class VoxelEditor; class Chunk; class ChunkManager; //This is where the main game components are contained // TODO(Ben): Dependency injection. class GameManager { public: static void initializeSystems(); static void registerTexturesForLoad(); static void getTextureHandles(); static void saveState(); static void savePlayerState(); static void clickDragRay(ChunkManager* chunkManager, Player* player, bool isBreakRay); static void scanWSO(ChunkManager* chunkManager, Player* player); static void onQuit(); static void endSession(); static class VoxelEditor* voxelEditor; static bool gameInitialized; static float fogStart, fogEnd; static class WSOAtlas* wsoAtlas; static class WSOScanner* wsoScanner; static class vg::TextureCache* textureCache; private: static bool _systemsInitialized; }; ================================================ FILE: SoA/GamePlayScreen.cpp ================================================ #include "stdafx.h" #include "GamePlayScreen.h" #include #include #include #include #include #include #include "App.h" #include "ChunkMesh.h" #include "ChunkMeshManager.h" #include "ChunkMesher.h" #include "ChunkRenderer.h" #include "Collision.h" #include "DebugRenderer.h" #include "DevConsole.h" #include "Errors.h" #include "GameManager.h" #include "GameSystem.h" #include "GameSystemUpdater.h" #include "HeadComponentUpdater.h" #include "InputMapper.h" #include "Inputs.h" #include "MainMenuScreen.h" #include "ParticleMesh.h" #include "SoaEngine.h" #include "SoaOptions.h" #include "SoAState.h" #include "SpaceSystem.h" #include "SpaceSystemUpdater.h" #include "TerrainPatch.h" #include "VRayHelper.h" #include "VoxelEditor.h" #include "soaUtils.h" GameplayScreen::GameplayScreen(const App* app, const MainMenuScreen* mainMenuScreen) : IAppScreen(app), m_mainMenuScreen(mainMenuScreen), m_soaState(nullptr), m_inputMapper(nullptr), controller(), m_spaceSystemUpdater(nullptr), m_gameSystemUpdater(nullptr), m_updateThread(nullptr), m_threadRunning(false), m_prevRenderState(nullptr), m_shouldReloadTarget(false), m_shouldReloadShaders(false), m_shouldToggleDevConsole(false){ // Empty } GameplayScreen::~GameplayScreen() { // Empty } i32 GameplayScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 GameplayScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void GameplayScreen::build() { // Empty } void GameplayScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Destruction happens in onExit } void GameplayScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { #ifdef VORB_OS_WINDOWS SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); #else struct sched_param params; params.sched_priority = sched_get_priority_max(SCHED_FIFO); pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶ms); #endif m_soaState = m_mainMenuScreen->getSoAState(); controller.startGame(m_soaState); initInput(); initConsole(); m_spaceSystemUpdater = std::make_unique(); m_gameSystemUpdater = std::make_unique(m_soaState, m_inputMapper); // Initialize the PDA m_pda.init(this); // Initialize the Pause Menu m_pauseMenu.init(this); // Set up the rendering initRenderPipeline(); // Initialize and run the update thread m_updateThread = new std::thread(&GameplayScreen::updateThreadFunc, this); SDL_SetRelativeMouseMode(SDL_TRUE); } void GameplayScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_inputMapper->stopInput(); m_hooks.dispose(); SoaEngine::destroyGameSystem(m_soaState); m_threadRunning = false; m_updateThread->join(); delete m_updateThread; m_pda.destroy(); //m_renderPipeline.destroy(true); m_pauseMenu.destroy(); m_devConsoleView.dispose(); } /// This update function runs on the render thread void GameplayScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { if (m_shouldReloadShaders) { m_renderer.reloadShaders(); m_shouldReloadShaders = false; } if (m_shouldToggleDevConsole) { m_shouldToggleDevConsole = false; DevConsole::getInstance().toggleFocus(); } m_spaceSystemUpdater->glUpdate(m_soaState); // TODO(Ben): Move to glUpdate for voxel component // TODO(Ben): Don't hardcode for a single player auto& vpCmp = m_soaState->gameSystem->voxelPosition.getFromEntity(m_soaState->clientState.playerEntity); m_soaState->clientState.chunkMeshManager->update(vpCmp.gridPosition.pos, true); // Update the PDA if (m_pda.isOpen()) m_pda.update(); // Updates the Pause Menu if (m_pauseMenu.isOpen()) m_pauseMenu.update(); // Check for target reload if (m_shouldReloadTarget) { m_reloadLock.lock(); printf("Reloading Target\n"); m_soaState->threadPool->clearTasks(); std::this_thread::sleep_for(std::chrono::microseconds(200)); SoaEngine::reloadSpaceBody(m_soaState, m_soaState->clientState.startingPlanet, nullptr); m_shouldReloadTarget = false; m_reloadLock.unlock(); } } void GameplayScreen::updateECS() { SpaceSystem* spaceSystem = m_soaState->spaceSystem; GameSystem* gameSystem = m_soaState->gameSystem; // Time warp const f64 TIME_WARP_SPEED = 100.0 + (f64)m_inputMapper->getInputState(INPUT_SPEED_TIME) * 1000.0; if (m_inputMapper->getInputState(INPUT_TIME_BACK)) { m_soaState->time -= TIME_WARP_SPEED; } if (m_inputMapper->getInputState(INPUT_TIME_FORWARD)) { m_soaState->time += TIME_WARP_SPEED; } m_soaState->time += m_soaState->timeStep; // TODO(Ben): Don't hardcode for a single player auto& spCmp = gameSystem->spacePosition.getFromEntity(m_soaState->clientState.playerEntity); auto parentNpCmpId = spaceSystem->sphericalGravity.get(spCmp.parentGravity).namePositionComponent; auto& parentNpCmp = spaceSystem->namePosition.get(parentNpCmpId); // Calculate non-relative space position f64v3 trueSpacePosition = spCmp.position + parentNpCmp.position; m_spaceSystemUpdater->update(m_soaState, trueSpacePosition, m_soaState->gameSystem->voxelPosition.getFromEntity(m_soaState->clientState.playerEntity).gridPosition.pos); m_gameSystemUpdater->update(gameSystem, spaceSystem, m_soaState); } void GameplayScreen::updateMTRenderState() { MTRenderState* state = m_renderStateManager.getRenderStateForUpdate(); SpaceSystem* spaceSystem = m_soaState->spaceSystem; GameSystem* gameSystem = m_soaState->gameSystem; // Set all space positions for (auto& it : spaceSystem->namePosition) { state->spaceBodyPositions[it.first] = it.second.position; } // Set camera position auto& spCmp = gameSystem->spacePosition.getFromEntity(m_soaState->clientState.playerEntity); state->spaceCameraPos = spCmp.position; state->spaceCameraOrientation = spCmp.orientation; // Set player data auto& physics = gameSystem->physics.getFromEntity(m_soaState->clientState.playerEntity); if (physics.voxelPosition) { state->playerHead = gameSystem->head.getFromEntity(m_soaState->clientState.playerEntity); state->playerPosition = gameSystem->voxelPosition.get(physics.voxelPosition); state->hasVoxelPos = true; } else { state->hasVoxelPos = false; } // Debug chunk grid if (m_renderer.stages.chunkGrid.isActive() && m_soaState->clientState.startingPlanet) { // TODO(Ben): This doesn't let you go to different planets!!! auto& svcmp = spaceSystem->sphericalVoxel.getFromEntity(m_soaState->clientState.startingPlanet); auto& vpCmp = gameSystem->voxelPosition.getFromEntity(m_soaState->clientState.playerEntity); state->debugChunkData.clear(); if (svcmp.chunkGrids) { for (ChunkHandle chunk : svcmp.chunkGrids[vpCmp.gridPosition.face].acquireActiveChunks()) { state->debugChunkData.emplace_back(); state->debugChunkData.back().genLevel = chunk->genLevel; state->debugChunkData.back().voxelPosition = chunk->getVoxelPosition().pos; } svcmp.chunkGrids[vpCmp.gridPosition.face].releaseActiveChunks(); } } else { std::vector().swap(state->debugChunkData); } m_renderStateManager.finishUpdating(); } void GameplayScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { globalRenderAccumulationTimer.start("Draw"); const MTRenderState* renderState; // Don't render the same state twice. if ((renderState = m_renderStateManager.getRenderStateForRender()) == m_prevRenderState) { return; } m_prevRenderState = renderState; // Set renderState and draw everything m_renderer.setRenderState(renderState); m_renderer.render(); globalRenderAccumulationTimer.stop(); // Draw dev console m_devConsoleView.update(0.01f); if (DevConsole::getInstance().isFocused()) { m_devConsoleView.render(m_game->getWindow().getViewportDims()); } // Uncomment to time rendering /* static int g = 0; if (++g == 10) { globalRenderAccumulationTimer.printAll(true); globalRenderAccumulationTimer.clear(); std::cout << "\n"; g = 0; }*/ } void GameplayScreen::unPause() { m_pauseMenu.close(); SDL_SetRelativeMouseMode(SDL_TRUE); m_soaState->isInputEnabled = true; } i32 GameplayScreen::getWindowWidth() const { return m_app->getWindow().getWidth(); } i32 GameplayScreen::getWindowHeight() const { return m_app->getWindow().getHeight(); } void GameplayScreen::initInput() { m_inputMapper = new InputMapper; initInputs(m_inputMapper); m_inputMapper->get(INPUT_PAUSE).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { SDL_SetRelativeMouseMode(SDL_FALSE); m_soaState->isInputEnabled = false; }); m_inputMapper->get(INPUT_GRID).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { m_renderer.toggleChunkGrid(); }); m_inputMapper->get(INPUT_INVENTORY).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { /* if (m_pda.isOpen()) { m_pda.close(); SDL_SetRelativeMouseMode(SDL_TRUE); m_soaState->isInputEnabled = true; SDL_StartTextInput(); } else { m_pda.open(); SDL_SetRelativeMouseMode(SDL_FALSE); m_soaState->isInputEnabled = false; SDL_StopTextInput(); }*/ SDL_SetRelativeMouseMode(SDL_FALSE); m_inputMapper->stopInput(); m_soaState->isInputEnabled = false; }); m_inputMapper->get(INPUT_NIGHT_VISION).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { if (isInGame()) { m_renderer.toggleNightVision(); } }); m_inputMapper->get(INPUT_HUD).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { m_renderer.cycleDevHud(); }); m_inputMapper->get(INPUT_NIGHT_VISION_RELOAD).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) -> void { m_renderer.loadNightVision(); }); m_inputMapper->get(INPUT_RELOAD_SHADERS).downEvent += makeDelegate(this, &GameplayScreen::onReloadShaders); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e VORB_MAYBE_UNUSED) { if (isInGame()) { SDL_SetRelativeMouseMode(SDL_TRUE); m_soaState->isInputEnabled = true; } }); m_inputMapper->get(INPUT_RELOAD_TARGET).downEvent += makeDelegate(this, &GameplayScreen::onReloadTarget); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&](Sender s VORB_UNUSED, const vui::MouseButtonEvent& e VORB_UNUSED) { if (GameManager::voxelEditor->isEditing()) { //TODO(Ben): Edit voxels } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e VORB_MAYBE_UNUSED) { SDL_SetRelativeMouseMode(SDL_TRUE); m_inputMapper->startInput(); m_soaState->isInputEnabled = true; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onFocusLost, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseEvent& e VORB_MAYBE_UNUSED) { SDL_SetRelativeMouseMode(SDL_FALSE); m_inputMapper->stopInput(); m_soaState->isInputEnabled = false; }); // Temporary dev console // TODO(Ben): Don't use functor vui::InputDispatcher::key.onKeyDown.addFunctor([&](Sender, const vui::KeyEvent& e VORB_UNUSED) { if (e.keyCode == VKEY_GRAVE) { m_shouldToggleDevConsole = true; if (!DevConsole::getInstance().isFocused()) { m_inputMapper->stopInput(); m_soaState->isInputEnabled = false; } else { m_inputMapper->startInput(); m_soaState->isInputEnabled = true; } } } ); { // Player movement events vecs::ComponentID parkourCmp = m_soaState->gameSystem->parkourInput.getComponentID(m_soaState->clientState.playerEntity); m_hooks.addAutoHook(m_inputMapper->get(INPUT_FORWARD).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveForward = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_FORWARD).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveForward = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_BACKWARD).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveBackward = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_BACKWARD).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveBackward = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_LEFT).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveLeft = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_LEFT).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveLeft = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_RIGHT).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveRight = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_RIGHT).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).moveRight = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_JUMP).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).jump = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_JUMP).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).jump = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_CROUCH).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).crouch = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_CROUCH).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).crouch = false; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_SPRINT).downEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).sprint = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_SPRINT).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).sprint = false; }); // TODO(Ben): Different parkour input m_hooks.addAutoHook(m_inputMapper->get(INPUT_SPRINT).downEvent, [=](Sender s VORB_UNUSED, ui32 a VORB_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).parkour = true; }); m_hooks.addAutoHook(m_inputMapper->get(INPUT_SPRINT).upEvent, [=](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_soaState->gameSystem->parkourInput.get(parkourCmp).parkour = false; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e VORB_MAYBE_UNUSED) { if (m_soaState->clientState.playerEntity) { vecs::EntityID pid = m_soaState->clientState.playerEntity; f64v3 pos = controller.getEntityEyeVoxelPosition(m_soaState, pid); f32v3 dir = controller.getEntityViewVoxelDirection(m_soaState, pid); auto& vp = m_soaState->gameSystem->voxelPosition.getFromEntity(pid); vecs::ComponentID svid = vp.parentVoxel; ChunkGrid& grid = m_soaState->spaceSystem->sphericalVoxel.get(svid).chunkGrids[vp.gridPosition.face]; VoxelRayFullQuery q = VRayHelper().getFullQuery(pos, dir, 100.0, grid); m_soaState->clientState.voxelEditor.setStartPosition(q.outer.location); std::cout << "DIST " << glm::length(f64v3(q.outer.location) - pos); printVec("Start ", q.outer.location); m_renderer.debugRenderer->drawLine(pos, pos + f64v3(dir) * 100.0, color::Red); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e VORB_MAYBE_UNUSED) { if (m_soaState->clientState.playerEntity) { vecs::EntityID pid = m_soaState->clientState.playerEntity; f64v3 pos = controller.getEntityEyeVoxelPosition(m_soaState, pid); f32v3 dir = controller.getEntityViewVoxelDirection(m_soaState, pid); auto& vp = m_soaState->gameSystem->voxelPosition.getFromEntity(pid); vecs::ComponentID svid = vp.parentVoxel; ChunkGrid& grid = m_soaState->spaceSystem->sphericalVoxel.get(svid).chunkGrids[vp.gridPosition.face]; VoxelRayFullQuery q = VRayHelper().getFullQuery(pos, dir, 100.0, grid); m_soaState->clientState.voxelEditor.setEndPosition(q.outer.location); printVec("End ", q.outer.location); ItemStack& iStack = m_soaState->gameSystem->inventory.getFromEntity(pid).items[0]; m_soaState->clientState.voxelEditor.editVoxels(grid, &iStack); } }); // Mouse movement vecs::ComponentID headCmp = m_soaState->gameSystem->head.getComponentID(m_soaState->clientState.playerEntity); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [=](Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e VORB_MAYBE_UNUSED) { HeadComponentUpdater::rotateFromMouse(m_soaState->gameSystem, headCmp, -e.dx, e.dy, 0.002f); }); } vui::InputDispatcher::window.onClose += makeDelegate(this, &GameplayScreen::onWindowClose); m_inputMapper->get(INPUT_SCREENSHOT).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.takeScreenshot(); }); m_inputMapper->get(INPUT_DRAW_MODE).downEvent += makeDelegate(this, &GameplayScreen::onToggleWireframe); m_inputMapper->startInput(); } void GameplayScreen::initConsole() { vui::GameWindow& window = m_game->getWindow(); // TODO(Ben): Dispose m_devConsoleView.init(&DevConsole::getInstance(), 5, f32v2(20.0f, window.getHeight() - 60.0f), window.getWidth() - 40.0f); DevConsole::getInstance().addCommand("exit"); DevConsole::getInstance().addListener("exit", [](void*, const nString&) { exit(0); }, nullptr); } void GameplayScreen::initRenderPipeline() { m_renderer.debugRenderer = m_soaState->clientState.debugRenderer; } // TODO(Ben): Collision //void GamePlayScreen::updatePlayer() { // m_player->update(m_inputManager, true, 0.0f, 0.0f); // Chunk **chunks = new Chunk*[8]; // _player->isGrounded = 0; // _player->setMoveMod(1.0f); // _player->canCling = 0; // _player->collisionData.yDecel = 0.0f; // // Number of steps to integrate the collision over // for (int i = 0; i < PLAYER_COLLISION_STEPS; i++){ // _player->gridPosition += (_player->velocity / (float)PLAYER_COLLISION_STEPS) * glSpeedFactor; // _player->facePosition += (_player->velocity / (float)PLAYER_COLLISION_STEPS) * glSpeedFactor; // _player->collisionData.clear(); // GameManager::voxelWorld->getClosestChunks(_player->gridPosition, chunks); //DANGER HERE! // aabbChunkCollision(_player, &(_player->gridPosition), chunks, 8); // _player->applyCollisionData(); // } // delete[] chunks; //} /// This is the update thread void GameplayScreen::updateThreadFunc() { m_threadRunning = true; #ifdef VORB_OS_WINDOWS SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); #else//VORB_OS_WINDOWS struct sched_param params; params.sched_priority = sched_get_priority_max(SCHED_FIFO); pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶ms); #endif//VORB_OS_WINDOWS FpsLimiter fpsLimiter; fpsLimiter.init(60.0f); // TODO(Matthew): This statement wasn't used, eventually log/display FPS. //f32 fps; static int saveStateTicks = SDL_GetTicks(); while (m_threadRunning) { fpsLimiter.beginFrame(); m_reloadLock.lock(); updateECS(); // TODO(Ben): Entity destruction in this thread calls opengl stuff..... updateMTRenderState(); m_reloadLock.unlock(); if (SDL_GetTicks() - saveStateTicks >= 20000) { saveStateTicks = SDL_GetTicks(); // savePlayerState(); } //fps = fpsLimiter.endFrame(); } } void GameplayScreen::onReloadShaders(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { printf("Reloading Shaders\n"); m_shouldReloadShaders = true; } void GameplayScreen::onReloadTarget(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_shouldReloadTarget = true; } void GameplayScreen::onQuit(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { SoaEngine::destroyAll(m_soaState); exit(0); } void GameplayScreen::onToggleWireframe(Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.toggleWireframe(); } void GameplayScreen::onWindowClose(Sender s) { onQuit(s, 0); } ================================================ FILE: SoA/GamePlayScreen.h ================================================ // // GamePlayScreen.h // Seed Of Andromeda // // Created by Ben Arnold on 17 Oct 2014 // Copyright 2014 Regrowth Studios // MIT License // // This file provides the implementation for the main // gameplay screen. This will encompass all in game // behavior. // #pragma once #ifndef GAMEPLAYSCREEN_H_ #define GAMEPLAYSCREEN_H_ #include #include #include "GameplayRenderer.h" #include "LoadMonitor.h" #include "MTRenderStateManager.h" #include "PDA.h" #include "PauseMenu.h" #include "SoaController.h" #include "DevConsoleView.h" #include "SpaceSystemUpdater.h" class App; class GameStartState; class GameSystem; class GameSystemUpdater; class InputMapper; class MainMenuScreen; struct SoaState; class SpriteBatch; class SpriteFont; class TerrainMeshMessage; template class GamePlayScreenDelegate; class OnPauseKeyDown; class OnFlyKeyDown; class OnGridKeyDown; class OnReloadTexturesKeyDown; class OnReloadShadersKeyDown; class OnInventoryKeyDown; class OnReloadUIKeyDown; class OnHUDKeyDown; // Each mode includes the previous mode enum DevUiModes { DEVUIMODE_NONE, DEVUIMODE_CROSSHAIR, DEVUIMODE_HANDS, DEVUIMODE_FPS, DEVUIMODE_ALL }; class GameplayScreen : public vui::IAppScreen { friend class GameplayRenderer; friend class GameplayLoadScreen; public: GameplayScreen(const App* app, const MainMenuScreen* mainMenuScreen); ~GameplayScreen(); virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; void unPause(); // Getters i32 getWindowWidth() const; i32 getWindowHeight() const; bool isInGame() const { return (!m_pda.isOpen() && !m_pauseMenu.isOpen()); } private: /// Initializes event delegates and InputManager void initInput(); /// Initializes dev console events void initConsole(); /// Initializes the rendering void initRenderPipeline(); /// The function that runs on the update thread. It handles /// loading the planet in the background. void updateThreadFunc(); /// Updates the Entity component system void updateECS(); /// Updates multi-threaded render state void updateMTRenderState(); // --------------- Event handlers --------------- void onReloadShaders(Sender s, ui32 a); void onReloadTarget(Sender s, ui32 a); void onQuit(Sender s, ui32 a); void onToggleWireframe(Sender s, ui32 i); void onWindowClose(Sender s); // ---------------------------------------------- const MainMenuScreen* m_mainMenuScreen; SoaState* m_soaState; InputMapper* m_inputMapper; PDA m_pda; ///< The PDA PauseMenu m_pauseMenu; ///< The Pause Menu DevConsoleView m_devConsoleView; SoaController controller; std::unique_ptr m_spaceSystemUpdater; std::unique_ptr m_gameSystemUpdater; std::thread* m_updateThread; ///< The thread that updates the planet. Runs updateThreadFunc() volatile bool m_threadRunning; ///< True when the thread should be running AutoDelegatePool m_hooks; ///< Input hooks reservoir GameplayRenderer m_renderer; ///< This handles all rendering for the screen MTRenderStateManager m_renderStateManager; ///< Manages the triple buffered render state const MTRenderState* m_prevRenderState; ///< Render state use for previous draw std::mutex m_reloadLock; bool m_shouldReloadTarget; bool m_shouldReloadShaders; bool m_shouldToggleDevConsole; }; #endif // GAMEPLAYSCREEN_H_ ================================================ FILE: SoA/GameRenderParams.cpp ================================================ #include "stdafx.h" #include "GameRenderParams.h" #include #include "Camera.h" #include "GameManager.h" void GameRenderParams::calculateParams(const f64v3& worldCameraPos VORB_UNUSED, const Camera* ChunkCamera, const VoxelPosition3D& voxPosition VORB_UNUSED, f64 voxelWorldRadius VORB_UNUSED, ChunkMeshManager* ChunkMeshmanager, BlockPack* blocks, BlockTexturePack* blockTexturePack, bool IsUnderwater) { chunkCamera = ChunkCamera; isUnderwater = IsUnderwater; this->blocks = blocks; this->blockTexturePack = blockTexturePack; chunkMeshmanager = ChunkMeshmanager; // Calculate fog sunlightDirection = glm::normalize(f32v3(0.7, 1.0, 0.5)); // float theta = (f32)glm::dot(VoxelSpaceConversions::voxelToWorld(voxPosition, voxelWorldRadius), f64v3(lightPos)); f32 theta = 1.0f; calculateFog(theta, isUnderwater); calculateSunlight(theta); sunlightColor = f32v3(1.0f); } void GameRenderParams::calculateFog(float theta, bool isUnderwater) { #define FOG_THETA_MULT 100.0f #define FOG_THETA_OFFSET 50.0f f32m4 VP; //********************************* TODO: PRECOMPILED HEADERS for compilation speed? float fogTheta = glm::clamp(theta, 0.0f, 1.0f); fogStart = 0; if (isUnderwater) { float underwaterColor = fogTheta / 2.0f; fogEnd = FOG_THETA_OFFSET + fogTheta * FOG_THETA_MULT; fogColor[0] = underwaterColor; fogColor[1] = underwaterColor; fogColor[2] = underwaterColor; } else { fogEnd = 100000; fogColor[0] = 1.0; fogColor[1] = 1.0; fogColor[2] = 1.0; } } void GameRenderParams::calculateSunlight(float theta) { // Calculate sunlight #define AMB_MULT 0.76f #define AMB_OFFSET 0.02f #define MIN_THETA 0.01f #define THETA_MULT 8.0f #define SUN_COLOR_MAP_HEIGHT 64.0f #define SUN_THETA_OFF 0.06f /* sunlightDirection = glm::normalize(f64v3(f64m4(glm::inverse(GameManager::player->worldRotationMatrix)) * f64m4(GameManager::planet->invRotationMatrix) * f64v4(1.0, 0.0, 0.0, 1.0))); */ float sunTheta = MAX(0.0f, theta + SUN_THETA_OFF); if (sunTheta > 1) sunTheta = 1; sunlightIntensity = sunTheta * AMB_MULT + AMB_OFFSET; if (sunlightIntensity > 1.0f) sunlightIntensity = 1.0f; float diffVal = 1.0f - sunlightIntensity; if (theta < MIN_THETA) { diffVal += (theta - MIN_THETA) * THETA_MULT; if (diffVal < 0.0f) diffVal = 0.0f; } // TODO(Matthew): This was previously not used, determine if we should use or throw it. // int sunHeight = (int)(theta * SUN_COLOR_MAP_HEIGHT); // if (theta < 0) { // sunHeight = 0; // } sunlightColor = f32v3(1.0f) * diffVal; } ================================================ FILE: SoA/GameRenderParams.h ================================================ #pragma once #ifndef GameRenderParams_h__ #define GameRenderParams_h__ #include #include "VoxelSpaceConversions.h" class ChunkMesh; class Camera; class ChunkMeshManager; class BlockPack; class BlockTexturePack; class GameRenderParams { public: void calculateParams(const f64v3& worldCameraPos, const Camera* ChunkCamera, const VoxelPosition3D& voxPosition, f64 voxelWorldRadius, ChunkMeshManager* ChunkMeshmanager, BlockPack* blocks, BlockTexturePack* blockTexturePack, bool IsUnderwater); f32v3 sunlightDirection; f32v3 sunlightColor; float sunlightIntensity; f32v3 fogColor; float fogEnd; float fogStart; float lightActive; const Camera* chunkCamera; ChunkMeshManager* chunkMeshmanager; BlockPack* blocks; BlockTexturePack* blockTexturePack; bool isUnderwater; private: void calculateFog(float theta, bool isUnderwater); void calculateSunlight(float theta); }; #endif // GameRenderParams_h__ ================================================ FILE: SoA/GameSystem.cpp ================================================ #include "stdafx.h" #include "GameSystem.h" GameSystem::GameSystem() : vecs::ECS() { //Add all component tables addComponentTable(GAME_SYSTEM_CT_AABBCOLLIDABLE_NAME, &aabbCollidable); addComponentTable(GAME_SYSTEM_CT_FREEMOVEINPUT_NAME, &freeMoveInput); addComponentTable(GAME_SYSTEM_CT_PARKOURINPUT_NAME, &parkourInput); addComponentTable(GAME_SYSTEM_CT_PHYSICS_NAME, &physics); addComponentTable(GAME_SYSTEM_CT_SPACEPOSITION_NAME, &spacePosition); addComponentTable(GAME_SYSTEM_CT_VOXELPOSITION_NAME, &voxelPosition); addComponentTable(GAME_SYSTEM_CT_FRUSTUM_NAME, &frustum); addComponentTable(GAME_SYSTEM_CT_HEAD_NAME, &head); addComponentTable(GAME_SYSTEM_CT_INVENTORY_NAME, &inventory); addComponentTable(GAME_SYSTEM_CT_CHUNKSPHERE_NAME, &chunkSphere); addComponentTable(GAME_SYSTEM_CT_ATTRIBUTES_NAME, &attributes); } vecs::ComponentID GameSystem::getComponent(const nString& name, vecs::EntityID eID) { auto& table = *getComponentTable(name); return table.getComponentID(eID); } ================================================ FILE: SoA/GameSystem.h ================================================ /// /// GameSystem.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 10 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Entity Component System for the main game entities. /// #pragma once #ifndef GameSystem_h__ #define GameSystem_h__ #include #include #include "GameSystemComponents.h" #define GAME_SYSTEM_CT_AABBCOLLIDABLE_NAME "AABBCollidable" #define GAME_SYSTEM_CT_FREEMOVEINPUT_NAME "FreeMoveInput" #define GAME_SYSTEM_CT_PARKOURINPUT_NAME "Parkour" #define GAME_SYSTEM_CT_PHYSICS_NAME "Physics" #define GAME_SYSTEM_CT_SPACEPOSITION_NAME "SpacePosition" #define GAME_SYSTEM_CT_VOXELPOSITION_NAME "VoxelPosition" #define GAME_SYSTEM_CT_FRUSTUM_NAME "Frustum" #define GAME_SYSTEM_CT_HEAD_NAME "Head" #define GAME_SYSTEM_CT_INVENTORY_NAME "Inventory" #define GAME_SYSTEM_CT_CHUNKSPHERE_NAME "ChunkSphere" #define GAME_SYSTEM_CT_ATTRIBUTES_NAME "Attributes" class GameSystem : public vecs::ECS { public: GameSystem(); AABBCollidableComponentTable aabbCollidable; FreeMoveInputComponentTable freeMoveInput; ParkourInputComponentTable parkourInput; PhysicsComponentTable physics; SpacePositionComponentTable spacePosition; VoxelPositionComponentTable voxelPosition; FrustumComponentTable frustum; HeadComponentTable head; InventoryComponentTable inventory; ChunkSphereComponentTable chunkSphere; AttributeComponentTable attributes; vecs::ComponentID getComponent(const nString& name, vecs::EntityID eID); private: VORB_NON_COPYABLE(GameSystem); }; #endif // GameSystem_h__ ================================================ FILE: SoA/GameSystemAssemblages.cpp ================================================ #include "stdafx.h" #include "GameSystemAssemblages.h" #include #include "ChunkHandle.h" #include "ChunkSphereComponentUpdater.h" #include "GameSystem.h" vecs::ComponentID GameSystemAssemblages::addFreeMoveInput(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID physicsComponent) { vecs::ComponentID vmCmpId = gameSystem->addComponent("FreeMove", entity); auto& vmCmp = gameSystem->freeMoveInput.get(vmCmpId); vmCmp.physicsComponent = physicsComponent; return vmCmpId; } void GameSystemAssemblages::removeFreeMoveInput(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("FreeMove", entity); } vecs::ComponentID GameSystemAssemblages::addPhysics(GameSystem* gameSystem, vecs::EntityID entity, f32 massKg, const f64v3& initialVel, vecs::ComponentID spacePositionComponent, vecs::ComponentID voxelPositionComponent /*= 0*/) { vecs::ComponentID pCmpId = gameSystem->addComponent("Physics", entity); auto& pCmp = gameSystem->physics.get(pCmpId); pCmp.spacePosition = spacePositionComponent; pCmp.voxelPosition = voxelPositionComponent; pCmp.velocity = initialVel; pCmp.mass = massKg; return pCmpId; } void GameSystemAssemblages::removePhysics(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("Physics", entity); } vecs::ComponentID GameSystemAssemblages::addSpacePosition(GameSystem* gameSystem, vecs::EntityID entity, const f64v3& position, const f64q& orientation, vecs::EntityID parentEntity, vecs::ComponentID parentGravComponent /* = 0 */, vecs::ComponentID parentSphericalTerrainComponent /* = 0 */) { vecs::ComponentID spCmpId = gameSystem->addComponent("SpacePosition", entity); auto& spCmp = gameSystem->spacePosition.get(spCmpId); spCmp.position = position; spCmp.orientation = orientation; spCmp.parentEntity = parentEntity; spCmp.parentGravity = parentGravComponent; spCmp.parentSphericalTerrain = parentSphericalTerrainComponent; return spCmpId; } void GameSystemAssemblages::removeSpacePosition(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("Space Position", entity); } vecs::ComponentID GameSystemAssemblages::addAabbCollidable(GameSystem* gameSystem, vecs::EntityID entity, const f32v3& box VORB_MAYBE_UNUSED, const f32v3& offset VORB_MAYBE_UNUSED) { vecs::ComponentID abCmpId = gameSystem->addComponent("AABBCollidable", entity); auto& abCmp = gameSystem->aabbCollidable.get(abCmpId); abCmp.box = f32v3(1.7f, 3.7f, 1.7f); abCmp.offset = f32v3(0.0f); return abCmpId; } void GameSystemAssemblages::removeAabbCollidable(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("AABBCollidable", entity); } vecs::ComponentID GameSystemAssemblages::addVoxelPosition(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID parentVoxelComponent, const f64q& orientation, const VoxelPosition3D& gridPosition) { // We need to transition to the voxels vecs::ComponentID vpid = gameSystem->addComponent("VoxelPosition", entity); auto& vpcmp = gameSystem->voxelPosition.get(vpid); vpcmp.parentVoxel = parentVoxelComponent; vpcmp.gridPosition = gridPosition; vpcmp.orientation = orientation; return vpid; } void GameSystemAssemblages::removeVoxelPosition(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("VoxelPosition", entity); } vecs::ComponentID GameSystemAssemblages::addChunkSphere(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID voxelPosition, const i32v3& centerPosition, ui32 radius) { vecs::ComponentID id = gameSystem->addComponent("ChunkSphere", entity); auto& cmp = gameSystem->chunkSphere.get(id); cmp.radius = radius; cmp.centerPosition = centerPosition; cmp.offset = i32v3(0); cmp.voxelPosition = voxelPosition; cmp.radius = radius; cmp.width = cmp.radius * 2 + 1; cmp.layer = cmp.width * cmp.width; cmp.size = cmp.layer * cmp.width; return id; } void GameSystemAssemblages::removeChunkSphere(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("ChunkSphere", entity); } extern vecs::ComponentID GameSystemAssemblages::addFrustumComponent(GameSystem* gameSystem, vecs::EntityID entity, f32 fov, f32 aspectRatio, f32 znear, f32 zfar, vecs::ComponentID spacePosition /* = 0 */, vecs::ComponentID voxelPosition /* = 0 */, vecs::ComponentID head /* = 0 */) { vecs::ComponentID fid = gameSystem->addComponent("Frustum", entity); auto& fcmp = gameSystem->frustum.get(fid); fcmp.frustum.setCamInternals(fov, aspectRatio, znear, zfar); fcmp.spacePosition = spacePosition; fcmp.voxelPosition = voxelPosition; fcmp.head = head; return fid; } extern void GameSystemAssemblages::removeFrustumComponent(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("Frustum", entity); } extern vecs::ComponentID GameSystemAssemblages::addHeadComponent(GameSystem* gameSystem, vecs::EntityID entity, f64 neckLength) { vecs::ComponentID hid = gameSystem->addComponent("Head", entity); auto& hcmp = gameSystem->head.get(hid); hcmp.neckLength = neckLength; return hid; } extern void GameSystemAssemblages::removeHeadComponent(GameSystem* gameSystem, vecs::EntityID entity) { gameSystem->deleteComponent("Head", entity); } ================================================ FILE: SoA/GameSystemAssemblages.h ================================================ /// /// GameSystemAssemblages.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Component and entity assemblages for GameSystem /// #pragma once #ifndef GameSystemAssemblages_h__ #define GameSystemAssemblages_h__ #include class ChunkAccessor; class GameSystem; #include "GameSystemComponents.h" namespace GameSystemAssemblages { /************************************************************************/ /* Component Factories */ /************************************************************************/ /// Free movement component vecs::ComponentID addFreeMoveInput(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID physicsComponent); void removeFreeMoveInput(GameSystem* gameSystem, vecs::EntityID entity); /// Physics component vecs::ComponentID addPhysics(GameSystem* gameSystem, vecs::EntityID entity, f32 massKg, const f64v3& initialVel, vecs::ComponentID spacePositionComponent, vecs::ComponentID voxelPositionComponent = 0); void removePhysics(GameSystem* gameSystem, vecs::EntityID entity); /// Space position component vecs::ComponentID addSpacePosition(GameSystem* gameSystem, vecs::EntityID entity, const f64v3& position, const f64q& orientation, vecs::EntityID parentEntity, vecs::ComponentID parentGravComponent = 0, vecs::ComponentID parentSphericalTerrainComponent = 0); void removeSpacePosition(GameSystem* gameSystem, vecs::EntityID entity); /// AABB Collision component vecs::ComponentID addAabbCollidable(GameSystem* gameSystem, vecs::EntityID entity, const f32v3& box, const f32v3& offset); void removeAabbCollidable(GameSystem* gameSystem, vecs::EntityID entity); /// Voxel Position Component vecs::ComponentID addVoxelPosition(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID parentVoxelComponent, const f64q& orientation, const VoxelPosition3D& gridPosition); void removeVoxelPosition(GameSystem* gameSystem, vecs::EntityID entity); /// Voxel Position Component vecs::ComponentID addChunkSphere(GameSystem* gameSystem, vecs::EntityID entity, vecs::ComponentID voxelPosition, const i32v3& centerPosition, ui32 radius); void removeChunkSphere(GameSystem* gameSystem, vecs::EntityID entity); /// Frustum Component vecs::ComponentID addFrustumComponent(GameSystem* gameSystem, vecs::EntityID entity, f32 fov, f32 aspectRatio, f32 znear, f32 zfar, vecs::ComponentID spacePosition = 0, vecs::ComponentID voxelPosition = 0, vecs::ComponentID head = 0); void removeFrustumComponent(GameSystem* gameSystem, vecs::EntityID entity); /// Head Component vecs::ComponentID addHeadComponent(GameSystem* gameSystem, vecs::EntityID entity, f64 neckLength); void removeHeadComponent(GameSystem* gameSystem, vecs::EntityID entity); } #endif // GameSystemAssemblages_h__ ================================================ FILE: SoA/GameSystemComponentBuilders.cpp ================================================ #include "stdafx.h" #include "GameSystemComponentBuilders.h" #include #include "GameSystemComponents.h" #include "Frustum.h" void AABBCollidableComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value component.box = f32v3(0.0f); component.offset = f32v3(0.0f); // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(AabbCollidableComponent)); } void AABBCollidableComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).aabbCollidable.get(m_cID) = component; } void AABBCollidableComponentBuilder::postBuild(vecs::ECS& ecs, vecs::EntityID eID) { GameSystem& gecs = static_cast(ecs); auto& cmp = gecs.aabbCollidable.getFromEntity(eID); cmp.physics = gecs.physics.getComponentID(eID); } void ParkourInputComponentBuilder::load(keg::ReadContext& context VORB_UNUSED, keg::Node node VORB_UNUSED) { // Do nothing } void ParkourInputComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).parkourInput.get(m_cID) = component; } void ParkourInputComponentBuilder::postBuild(vecs::ECS& ecs, vecs::EntityID eID) { GameSystem& gecs = static_cast(ecs); auto& cmp = gecs.parkourInput.getFromEntity(eID); cmp.physicsComponent = gecs.physics.getComponentID(eID); cmp.attributeComponent = gecs.attributes.getComponentID(eID); cmp.headComponent = gecs.head.getComponentID(eID); cmp.aabbCollidable = gecs.aabbCollidable.getComponentID(eID); } void AttributeComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(AttributeComponent)); } void AttributeComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).attributes.get(m_cID) = component; } void SpacePositionComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value component.position = f64v3(0.0f); component.orientation = f64q(0.0, 0.0, 0.0, 1.0); component.parentEntity = 0; component.parentGravity = 0; component.parentSphericalTerrain = 0; // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(SpacePositionComponent)); } void SpacePositionComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).spacePosition.get(m_cID) = component; } void VoxelPositionComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value component.orientation = f64q(0.0, 0.0, 0.0, 1.0); VoxelPosition3D pos; pos.pos = f64v3(0.0); pos.face = FACE_TOP; component.gridPosition = pos; component.parentVoxel = 0; // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(VoxelPositionComponent)); } void VoxelPositionComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).voxelPosition.get(m_cID) = component; } void PhysicsComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value component.velocity = f64v3(0.0); component.mass = 1.0; component.spacePosition = 0; component.voxelPosition = 0; // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(PhysicsComponent)); } void PhysicsComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).physics.get(m_cID) = component; } void PhysicsComponentBuilder::postBuild(vecs::ECS& ecs, vecs::EntityID eID) { GameSystem& gecs = static_cast(ecs); auto& cmp = gecs.physics.getFromEntity(eID); cmp.voxelPosition = gecs.voxelPosition.getComponentID(eID); } void FrustumComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value Frustum frustum; frustum.setCamInternals(1.0f, 16.0f / 9.0f, 0.1f, 100.0f); component.frustum = frustum; component.spacePosition = 0; component.voxelPosition = 0; component.head = 0; // Simple read keg::parse((ui8*)&frustum, node, context, &KEG_GLOBAL_TYPE(FrustumComponent)); component.frustum = frustum; } void FrustumComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).frustum.get(m_cID) = component; } void HeadComponentBuilder::load(keg::ReadContext& context, keg::Node node) { // Default value component.neckLength = 0.0; component.relativeOrientation = f64q(0.0, 0.0, 0.0, 1.0); component.relativePosition = f64v3(0.0); // Simple read keg::parse((ui8*)&component, node, context, &KEG_GLOBAL_TYPE(FrustumComponent)); } void HeadComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).head.get(m_cID) = component; } void HeadComponentBuilder::postBuild(vecs::ECS& ecs, vecs::EntityID eID) { GameSystem& gecs = static_cast(ecs); auto& cmp = gecs.head.getFromEntity(eID); cmp.voxelPosition = gecs.voxelPosition.getComponentID(eID); } void InventoryComponentBuilder::load(keg::ReadContext& reader VORB_UNUSED, keg::Node node VORB_UNUSED) { // TODO(Ben): Load another file using a file path } void InventoryComponentBuilder::build(vecs::ECS& ecs, vecs::EntityID eID VORB_MAYBE_UNUSED) { ((GameSystem&)ecs).inventory.get(m_cID) = component; } ================================================ FILE: SoA/GameSystemComponentBuilders.h ================================================ #pragma once #ifndef GameSystemComponentBuilders_h__ #define GameSystemComponentBuilders_h__ #include #include "GameSystem.h" #include "GameSystemComponents.h" #include "ECSTemplates.h" class AABBCollidableComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; virtual void postBuild(vecs::ECS& ecs, vecs::EntityID eID) override; AabbCollidableComponent component; }; class ParkourInputComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; virtual void postBuild(vecs::ECS& ecs, vecs::EntityID eID) override; ParkourInputComponent component; }; class AttributeComponentBuilder : public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; AttributeComponent component; }; /* TODO(BEN): Implement these component builders class FreeMoveInputComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; FreeMoveInputComponent component; }; */ class SpacePositionComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; SpacePositionComponent component; }; class VoxelPositionComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; VoxelPositionComponent component; }; class PhysicsComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; virtual void postBuild(vecs::ECS& ecs, vecs::EntityID eID) override; PhysicsComponent component; }; class FrustumComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; FrustumComponent component; }; class HeadComponentBuilder: public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; virtual void postBuild(vecs::ECS& ecs, vecs::EntityID eID) override; HeadComponent component; }; class InventoryComponentBuilder : public ECSComponentBuilder { public: virtual void load(keg::ReadContext& reader, keg::Node node) override; virtual void build(vecs::ECS& ecs, vecs::EntityID eID) override; InventoryComponent component; }; #endif //GameSystemComponentBuilders_h__ ================================================ FILE: SoA/GameSystemComponents.cpp ================================================ #include "stdafx.h" #include "GameSystemComponents.h" KEG_TYPE_DEF(AabbCollidableComponent, AabbCollidableComponent, kt) { using namespace keg; kt.addValue("box", Value::basic(offsetOf(AabbCollidableComponent, box), BasicType::F32_V3)); kt.addValue("offset", Value::basic(offsetOf(AabbCollidableComponent, offset), BasicType::F32_V3)); } KEG_TYPE_DEF(AttributeComponent, AttributeComponent, kt) { using namespace keg; kt.addValue("strength", Value::basic(offsetOf(AttributeComponent, strength), BasicType::F64)); kt.addValue("perception", Value::basic(offsetOf(AttributeComponent, perception), BasicType::F64)); kt.addValue("endurance", Value::basic(offsetOf(AttributeComponent, endurance), BasicType::F64)); kt.addValue("charisma", Value::basic(offsetOf(AttributeComponent, charisma), BasicType::F64)); kt.addValue("intelligence", Value::basic(offsetOf(AttributeComponent, intelligence), BasicType::F64)); kt.addValue("agility", Value::basic(offsetOf(AttributeComponent, agility), BasicType::F64)); kt.addValue("height", Value::basic(offsetOf(AttributeComponent, perception), BasicType::F64)); } /* //TODO(BEN): Properly implement this KEG_TYPE_DEF(FreeMoveInputComponent, FreeMoveInputComponent, kt) { using namespace keg; kt.addValue("Speed", Value::basic(offsetof(FreeMoveInputComponent, speed), BasicType::F32)); }*/ KEG_TYPE_DEF(SpacePositionComponent, SpacePositionComponent, kt) { using namespace keg; kt.addValue("position", Value::basic(offsetof(SpacePositionComponent, position), BasicType::F64_V3)); kt.addValue("orientation", Value::basic(offsetof(SpacePositionComponent, orientation), BasicType::F64_V4)); } KEG_TYPE_DEF(VoxelPositionComponent, VoxelPositionComponent, kt) { using namespace keg; kt.addValue("orientation", Value::basic(offsetof(VoxelPositionComponent, orientation), BasicType::F64_V4)); //kt.addValue("GridPosition", Value::value(&VoxelPositionComponent::gridPosition)); kt.addValue("voxelPosition", Value::basic(offsetof(VoxelPositionComponent, gridPosition), BasicType::F64_V3)); kt.addValue("worldCubeFace", Value::basic(offsetof(VoxelPositionComponent, gridPosition) + offsetof(VoxelPosition3D, face), BasicType::ENUM)); } KEG_TYPE_DEF(PhysicsComponent, PhysicsComponent, kt) { using namespace keg; kt.addValue("velocity", Value::basic(offsetof(PhysicsComponent, velocity), BasicType::F64_V3)); kt.addValue("mass", Value::basic(offsetof(PhysicsComponent, mass), BasicType::F32)); } KEG_TYPE_DEF(FrustumComponent, FrustumComponent, kt) { using namespace keg; kt.addValue("fov", Value::basic(0, BasicType::F32)); kt.addValue("aspectRatio", Value::basic(sizeof(f32), BasicType::F32)); kt.addValue("zNear", Value::basic(sizeof(f32) * 2, BasicType::F32)); kt.addValue("zFar", Value::basic(sizeof(f32) *3, BasicType::F32)); } KEG_TYPE_DEF(HeadComponent, HeadComponent, kt) { using namespace keg; kt.addValue("relOrientation", Value::basic(offsetof(HeadComponent, relativeOrientation), BasicType::F64_V4)); kt.addValue("relPosition", Value::basic(offsetof(HeadComponent, relativePosition), BasicType::F64_V3)); kt.addValue("neckLength", Value::basic(offsetof(HeadComponent, neckLength), BasicType::F64)); } ================================================ FILE: SoA/GameSystemComponents.h ================================================ /// /// GameSystemComponents.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Components for game system /// #pragma once #ifndef GameSystemComponents_h__ #define GameSystemComponents_h__ #include #include #include #include #include "BlockData.h" #include "ChunkHandle.h" #include "Frustum.h" #include "VoxelCoordinateSpaces.h" class ChunkAccessor; class ChunkGrid; struct BlockCollisionData { BlockCollisionData(BlockID id, ui16 index) : id(id), index(index), neighborCollideFlags(0) {} BlockID id; ui16 index; union { struct { bool left : 1; bool right : 1; bool bottom : 1; bool top : 1; bool back : 1; bool front : 1; }; ui8 neighborCollideFlags; }; }; struct AabbCollidableComponent { vecs::ComponentID physics; std::map> voxelCollisions; // TODO(Ben): Entity-Entity collision f32v3 box = f32v3(0.0f); ///< x, y, z widths in blocks f32v3 offset = f32v3(0.0f); ///< x, y, z offsets in blocks }; typedef vecs::ComponentTable AABBCollidableComponentTable; KEG_TYPE_DECL(AabbCollidableComponent); struct AttributeComponent { f64 strength = 0.0; f64 perception = 0.0; f64 endurance = 0.0; f64 charisma = 0.0; f64 intelligence = 0.0; f64 agility = 0.0; f64 height = 4.0; }; typedef vecs::ComponentTable AttributeComponentTable; KEG_TYPE_DECL(AttributeComponent); struct ParkourInputComponent { // Bitfield inputs union { struct { bool moveForward : 1; ///< True when moving forward bool moveBackward : 1; ///< True when moving backward bool moveLeft : 1; ///< True when moving left bool moveRight : 1; ///< True when moving right bool jump : 1; ///< True when attempting to jump bool crouch : 1; ///< True when attempting to crouch bool parkour : 1; ///< True when parkouring bool sprint : 1; ///< True when sprinting }; ui8 moveFlags = 0; }; vecs::ComponentID aabbCollidable; vecs::ComponentID physicsComponent; vecs::ComponentID attributeComponent; vecs::ComponentID headComponent; }; typedef vecs::ComponentTable ParkourInputComponentTable; struct FreeMoveInputComponent { // Bitfield inputs union { struct { bool tryMoveForward : 1; ///< True when moving forward bool tryMoveBackward : 1; ///< True when moving backward bool tryMoveLeft : 1; ///< True when moving left bool tryMoveRight : 1; ///< True when moving right bool tryMoveUp : 1; ///< True when attempting to go up bool tryMoveDown : 1; ///< True when attempting to go down bool superSpeed : 1; ///< True when super speed is active bool tryRollLeft : 1; ///< True when trying to roll left along cam Z bool tryRollRight : 1; ///< True when trying to roll right along cam Z }; ui16 moveFlags = 0; }; vecs::ComponentID physicsComponent = 0; float speed = 0.3f; }; typedef vecs::ComponentTable FreeMoveInputComponentTable; //KEG_TYPE_DECL(FreeMoveInputComponent); struct SpacePositionComponent { f64v3 position = f64v3(0.0); f64q orientation; vecs::EntityID parentEntity = 0; vecs::ComponentID parentGravity = 0; ///< Gravity component of parent system body vecs::ComponentID parentSphericalTerrain = 0; ///< ST component of parent system body }; typedef vecs::ComponentTable SpacePositionComponentTable; KEG_TYPE_DECL(SpacePositionComponent); typedef f64v3 VoxelPosition; struct VoxelPositionComponent { f64q orientation; f64v3 eulerAngles = f64v3(0.0); ///< Pitch, Yaw, Roll in radians. VoxelPosition3D gridPosition; vecs::ComponentID parentVoxel = 0; }; typedef vecs::ComponentTable VoxelPositionComponentTable; KEG_TYPE_DECL(VoxelPositionComponent); struct ChunkSphereComponent { vecs::ComponentID voxelPosition; std::vector activeChunks; // TODO(Ben): Chunk position? i32v3 offset = i32v3(0); i32v3 centerPosition = i32v3(0); ChunkGrid* chunkGrid = nullptr; ChunkHandle* handleGrid = nullptr; // For fast 1 chunk shift std::vector acquireOffsets; WorldCubeFace currentCubeFace = FACE_NONE; i32 radius = 0; i32 width = 0; i32 layer = 0; i32 size = 0; }; class ChunkSphereComponentTable : public vecs::ComponentTable { public: virtual void disposeComponent(vecs::ComponentID cID, vecs::EntityID eID VORB_MAYBE_UNUSED) override { ChunkSphereComponent& cmp = _components[cID].second; delete[] cmp.handleGrid; cmp.handleGrid = nullptr; cmp.chunkGrid = nullptr; } }; struct PhysicsComponent { f64v3 velocity = f64v3(0.0); f32 mass; vecs::ComponentID spacePosition = 0; ///< Optional vecs::ComponentID voxelPosition = 0; ///< Optional }; typedef vecs::ComponentTable PhysicsComponentTable; KEG_TYPE_DECL(PhysicsComponent); struct FrustumComponent { Frustum frustum; vecs::ComponentID spacePosition = 0; ///< Optional vecs::ComponentID voxelPosition = 0; ///< Optional vecs::ComponentID head = 0; ///< Optional }; typedef vecs::ComponentTable FrustumComponentTable; KEG_TYPE_DECL(FrustumComponent); struct HeadComponent { vecs::ComponentID voxelPosition; f64q relativeOrientation; f64v3 eulerAngles = f64v3(0.0); ///< Pitch, Yaw, Roll in radians. f64v3 angleToApply = f64v3(0.0); f64v3 relativePosition = f64v3(0.0); ///< Position in voxel units relative to entity position f64 neckLength = 0.0; ///< Neck length in voxel units }; typedef vecs::ComponentTable HeadComponentTable; KEG_TYPE_DECL(HeadComponent); struct InventoryComponent { std::vector items; ///< Who needs fast lookups? }; typedef vecs::ComponentTable InventoryComponentTable; #endif // GameSystemComponents_h__ ================================================ FILE: SoA/GameSystemUpdater.cpp ================================================ #include "stdafx.h" #include "GameSystemUpdater.h" #include "FreeMoveComponentUpdater.h" #include "GameSystem.h" #include "GameSystemAssemblages.h" #include "Inputs.h" #include "SoAState.h" #include "SpaceSystem.h" #include "SpaceSystemAssemblages.h" #include "TerrainPatch.h" #include "VoxelSpaceConversions.h" #include "VoxelSpaceUtils.h" #include GameSystemUpdater::GameSystemUpdater(OUT SoaState* soaState, InputMapper* inputMapper) : m_soaState(soaState), m_inputMapper(inputMapper) { } GameSystemUpdater::~GameSystemUpdater() { } void GameSystemUpdater::update(OUT GameSystem* gameSystem, OUT SpaceSystem* spaceSystem, const SoaState* soaState VORB_MAYBE_UNUSED) { // Update component tables m_freeMoveUpdater.update(gameSystem, spaceSystem); m_headUpdater.update(gameSystem); m_aabbCollidableUpdater.update(gameSystem, spaceSystem); m_parkourUpdater.update(gameSystem, spaceSystem, soaState); m_physicsUpdater.update(gameSystem, spaceSystem); m_collisionUpdater.update(gameSystem); m_chunkSphereUpdater.update(gameSystem, spaceSystem); m_frustumUpdater.update(gameSystem); } ================================================ FILE: SoA/GameSystemUpdater.h ================================================ /// /// GameSystemUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates a GameSystem ECS /// #pragma once #ifndef GameSystemUpdater_h__ #define GameSystemUpdater_h__ #include "ChunkSphereComponentUpdater.h" #include "CollisionComponentUpdater.h" #include "FreeMoveComponentUpdater.h" #include "FrustumComponentUpdater.h" #include "InputMapper.h" #include "PhysicsComponentUpdater.h" #include "ParkourComponentUpdater.h" #include "AABBCollidableComponentUpdater.h" #include "HeadComponentUpdater.h" #include "VoxelCoordinateSpaces.h" #include #include struct SoaState; class SpaceSystem; struct VoxelPositionComponent; struct SoaState; DECL_VVOX(class VoxelPlanetMapData); class GameSystemUpdater { friend class GameSystemEvents; public: GameSystemUpdater(OUT SoaState* soaState, InputMapper* inputMapper); ~GameSystemUpdater(); /// Updates the game system, and also updates voxel components for space system /// planet transitions. /// @param gameSystem: Game ECS /// @param spaceSystem: Space ECS. Only SphericalVoxelComponents are modified. void update(OUT GameSystem* gameSystem, OUT SpaceSystem* spaceSystem, const SoaState* soaState); private: int m_frameCounter = 0; ///< Counts frames for updateVoxelPlanetTransitions updates /// Updaters PhysicsComponentUpdater m_physicsUpdater; CollisionComponentUpdater m_collisionUpdater; FreeMoveComponentUpdater m_freeMoveUpdater; HeadComponentUpdater m_headUpdater; AABBCollidableComponentUpdater m_aabbCollidableUpdater; ParkourComponentUpdater m_parkourUpdater; ChunkSphereComponentUpdater m_chunkSphereUpdater; FrustumComponentUpdater m_frustumUpdater; const SoaState* m_soaState = nullptr; InputMapper* m_inputMapper = nullptr; }; #endif // GameSystemUpdater_h__ ================================================ FILE: SoA/GameplayLoadScreen.cpp ================================================ #include "stdafx.h" #include "GameplayLoadScreen.h" #include "App.h" #include "CommonState.h" #include "GamePlayScreen.h" #include "LoadTaskBlockData.h" #include "MainMenuScreen.h" #include "LoadTaskTextures.h" #include "SoaEngine.h" #include "SoAState.h" GameplayLoadScreen::GameplayLoadScreen(const App* app, CommonState* state, MainMenuScreen* mainMenuScreen, GameplayScreen* gameplayScreen) : IAppScreen(app), m_commonState(state), m_mainMenuScreen(mainMenuScreen), m_gameplayScreen(gameplayScreen) { // Empty } i32 GameplayLoadScreen::getNextScreen() const { return m_app->scrGamePlay->getIndex(); } i32 GameplayLoadScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void GameplayLoadScreen::build() { // Empty } void GameplayLoadScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void GameplayLoadScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { addLoadTask("BlockData", new LoadTaskBlockData(&m_commonState->state->blocks, &m_commonState->state->clientState.blockTextureLoader, &m_commonState->loadContext)); // addLoadTask("Textures", new LoadTaskTextures); // m_monitor.setDep("Textures", "BlockData"); m_gameplayScreen->m_renderer.init(m_commonState->window, m_commonState->loadContext, m_gameplayScreen, m_commonState); m_gameplayScreen->m_renderer.hook(); m_commonState->loadContext.begin(); m_gameplayScreen->m_renderer.load(m_commonState->loadContext); // Start the tasks m_monitor.start(); } void GameplayLoadScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // Dispose our borrowed renderer m_mainMenuScreen->m_renderer.dispose(m_commonState->loadContext); // Disable main menu viewer m_commonState->stages.spaceSystem.setSystemViewer(nullptr); m_commonState->loadContext.end(); } void GameplayLoadScreen::update(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // Perform OpenGL calls m_glrpc.processRequests(1); m_commonState->loadContext.processRequests(1); m_gameplayScreen->m_renderer.updateGL(); // Defer texture loading static bool loadedTextures = false; // End condition if (!loadedTextures && m_gameplayScreen->m_renderer.isLoaded() && m_monitor.isTaskFinished("BlockData")) { // Post process the planets for (auto& it : m_commonState->state->spaceSystem->sphericalTerrain) { // auto& cmp = it.second; SoaEngine::initVoxelGen(it.second.planetGenData, m_commonState->state->blocks); } m_commonState->state->clientState.blockTextures->update(); m_commonState->state->clientState.blockTextures->writeDebugAtlases(); //m_commonState->state->blockTextures->save(&m_commonState->state->blocks); m_state = vui::ScreenState::CHANGE_NEXT; loadedTextures = true; } } void GameplayLoadScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_commonState->state->clientState.spaceCamera.updateProjection(); m_mainMenuScreen->m_renderer.render(); } void GameplayLoadScreen::addLoadTask(const nString& name, ILoadTask* task) { // Add the load task to the monitor m_loadTasks.push_back(task); m_monitor.addTask(name, m_loadTasks.back()); } ================================================ FILE: SoA/GameplayLoadScreen.h ================================================ /// /// GameplayLoadScreen.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 6 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Load screen for going into a game world /// #pragma once #ifndef GameplayLoadScreen_h__ #define GameplayLoadScreen_h__ #include #include #include #include #include #include "LoadMonitor.h" class App; class MainMenuScreen; class GameplayScreen; struct CommonState; #define VORB_NUM_TEXTURES 7 #define REGROWTH_NUM_TEXTURES 2 class GameplayLoadScreen : public vui::IAppScreen < App > { public: GameplayLoadScreen(const App* app, CommonState* state, MainMenuScreen* mainMenuScreen, GameplayScreen* gameplayScreen); virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: void addLoadTask(const nString& name, ILoadTask* task); // Game state CommonState* m_commonState = nullptr; MainMenuScreen* m_mainMenuScreen = nullptr; GameplayScreen* m_gameplayScreen = nullptr; // Loading Tasks LoadMonitor m_monitor; std::vector m_loadTasks; vcore::RPCManager m_glrpc; ///< Handles cross-thread OpenGL calls }; #endif // GameplayLoadScreen_h__ ================================================ FILE: SoA/GameplayRenderer.cpp ================================================ #include "stdafx.h" #include "GameplayRenderer.h" #include #include #include #include "ChunkMeshManager.h" #include "CommonState.h" #include "DebugRenderer.h" #include "Errors.h" #include "GameSystem.h" #include "GamePlayScreen.h" #include "MTRenderState.h" #include "PauseMenu.h" #include "SoAState.h" #include "SoaOptions.h" #include "SpaceSystem.h" #include "soaUtils.h" #define DEVHUD_FONT_SIZE 32 void GameplayRenderer::init(vui::GameWindow* window, StaticLoadContext& context, GameplayScreen* gameplayScreen, CommonState* commonState) { m_window = window; m_gameplayScreen = gameplayScreen; m_commonState = commonState; m_state = m_commonState->state; // TODO(Ben): Dis is bad mkay m_viewport = f32v4(0, 0, m_window->getWidth(), m_window->getHeight()); // Get window dimensions f32v2 windowDims(m_viewport.z, m_viewport.w); m_state->clientState.spaceCamera.setAspectRatio(windowDims.x / windowDims.y); m_voxelCamera.setAspectRatio(windowDims.x / windowDims.y); // Init Stages stages.opaqueVoxel.init(window, context); stages.cutoutVoxel.init(window, context); stages.chunkGrid.init(window, context); stages.transparentVoxel.init(window, context); stages.liquidVoxel.init(window, context); stages.devHud.init(window, context); stages.pda.init(window, context); stages.pauseMenu.init(window, context); stages.nightVision.init(window, context); stages.ssao.init(window, context); stages.bloom.init(window, context); stages.bloom.setParams(); stages.exposureCalc.init(window, context); loadNightVision(); // No post-process effects to begin with stages.bloom.setActive(true); stages.nightVision.setActive(false); stages.chunkGrid.setActive(true); // TODO(Ben): Temporary //stages.chunkGrid.setActive(false); } void GameplayRenderer::setRenderState(const MTRenderState* renderState) { m_renderState = renderState; } void GameplayRenderer::dispose(StaticLoadContext& context) { // Kill the builder if (m_loadThread) { delete m_loadThread; m_loadThread = nullptr; } stages.opaqueVoxel.dispose(context); stages.cutoutVoxel.dispose(context); stages.chunkGrid.dispose(context); stages.transparentVoxel.dispose(context); stages.liquidVoxel.dispose(context); stages.devHud.dispose(context); stages.pda.dispose(context); stages.pauseMenu.dispose(context); stages.nightVision.dispose(context); stages.ssao.dispose(context); stages.bloom.dispose(context); stages.exposureCalc.dispose(context); // dispose of persistent rendering resources m_hdrTarget.dispose(); m_swapChain.dispose(); } void GameplayRenderer::reloadShaders() { // TODO(Ben): More StaticLoadContext context; m_chunkRenderer.dispose(); m_chunkRenderer.init(); m_commonState->stages.spaceSystem.reloadShaders(); stages.ssao.reloadShaders(); } void GameplayRenderer::load(StaticLoadContext& context) { m_isLoaded = false; m_loadThread = new std::thread([&]() { vcore::GLRPC so[4]; size_t i = 0; // Create the HDR target so[i].set([&](Sender, void*) { Array attachments; vg::GBufferAttachment att[2]; // TODO(Ben): Don't think this is right. // Color att[0].format = vg::TextureInternalFormat::RGBA16F; att[0].pixelFormat = vg::TextureFormat::RGBA; att[0].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[0].number = 1; // Normals att[1].format = vg::TextureInternalFormat::RGBA16F; att[1].pixelFormat = vg::TextureFormat::RGBA; att[1].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[1].number = 2; m_hdrTarget.setSize(m_window->getWidth(), m_window->getHeight()); m_hdrTarget.init(Array(att, 2), vg::TextureInternalFormat::RGBA16F).initDepth(); if (soaOptions.get(OPT_MSAA).value.i > 0) { glEnable(GL_MULTISAMPLE); } else { glDisable(GL_MULTISAMPLE); } }); m_glrpc.invoke(&so[i++], false); // Create the swap chain for post process effects (HDR-capable) so[i].set([&](Sender, void*) { m_swapChain.init(m_window->getWidth(), m_window->getHeight(), vg::TextureInternalFormat::RGBA16F); }); m_glrpc.invoke(&so[i++], false); // Initialize the chunk renderer so[i].set([&](Sender, void*) { m_chunkRenderer.init(); }); m_glrpc.invoke(&so[i++], false); // Wait for the last command to complete so[i - 1].block(); // Load all the stages stages.opaqueVoxel.load(context); stages.cutoutVoxel.load(context); stages.chunkGrid.load(context); stages.transparentVoxel.load(context); stages.liquidVoxel.load(context); stages.devHud.load(context); stages.pda.load(context); stages.pauseMenu.load(context); stages.nightVision.load(context); stages.ssao.load(context); stages.bloom.load(context); stages.exposureCalc.load(context); m_isLoaded = true; }); m_loadThread->detach(); } void GameplayRenderer::hook() { // Note: Common stages are hooked in MainMenuRenderer, no need to re-hook // Grab mesh manager handle m_meshManager = m_state->clientState.chunkMeshManager; stages.opaqueVoxel.hook(&m_chunkRenderer, &m_gameRenderParams); stages.cutoutVoxel.hook(&m_chunkRenderer, &m_gameRenderParams); stages.transparentVoxel.hook(&m_chunkRenderer, &m_gameRenderParams); stages.liquidVoxel.hook(&m_chunkRenderer, &m_gameRenderParams); stages.chunkGrid.hook(&m_gameRenderParams); //stages.devHud.hook(); //stages.pda.hook(); stages.pauseMenu.hook(&m_gameplayScreen->m_pauseMenu); stages.nightVision.hook(&m_commonState->quad); stages.ssao.hook(&m_commonState->quad, m_window->getWidth(), m_window->getHeight()); stages.bloom.hook(&m_commonState->quad); stages.exposureCalc.hook(&m_commonState->quad, &m_hdrTarget, &m_viewport, 1024); m_commonState->stages.spaceSystem.setFarTerrainCamera(&m_voxelCamera); } void GameplayRenderer::updateGL() { // TODO(Ben): Experiment with more requests m_glrpc.processRequests(1); } void GameplayRenderer::render() { // const GameSystem* gameSystem = m_state->gameSystem; // const SpaceSystem* spaceSystem = m_state->spaceSystem; updateCameras(); // Set up the gameRenderParams const GameSystem* gs = m_state->gameSystem; // Get the physics component auto& phycmp = gs->physics.getFromEntity(m_state->clientState.playerEntity); VoxelPosition3D pos; if (phycmp.voxelPosition) { pos = gs->voxelPosition.get(phycmp.voxelPosition).gridPosition; } // TODO(Ben): Is this causing the camera slide discrepancy? SHouldn't we use MTRenderState? m_gameRenderParams.calculateParams(m_state->clientState.spaceCamera.getPosition(), &m_voxelCamera, pos, 100, m_meshManager, &m_state->blocks, m_state->clientState.blockTextures, false); // Bind the FBO m_hdrTarget.useGeometry(); glClear(GL_DEPTH_BUFFER_BIT); // worldCamera passes m_commonState->stages.skybox.render(&m_state->clientState.spaceCamera); if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); m_commonState->stages.spaceSystem.setShowAR(false); m_commonState->stages.spaceSystem.setRenderState(m_renderState); m_commonState->stages.spaceSystem.render(&m_state->clientState.spaceCamera); if (m_renderState->hasVoxelPos) { glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); stages.opaqueVoxel.render(&m_voxelCamera); // _physicsBlockRenderStage->draw(); // m_cutoutVoxelRenderStage->render(); // auto& voxcmp = gameSystem->voxelPosition.getFromEntity(m_state->clientState.playerEntity).parentVoxel; stages.chunkGrid.setState(m_renderState); stages.chunkGrid.render(&m_voxelCamera); // m_liquidVoxelRenderStage->render(); // m_transparentVoxelRenderStage->render(); if (debugRenderer) { debugRenderer->render(m_voxelCamera.getViewProjectionMatrix(), m_voxelCamera.getPosition()); } } if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Check for face transition animation state if (m_commonState->stages.spaceSystem.needsFaceTransitionAnimation) { m_commonState->stages.spaceSystem.needsFaceTransitionAnimation = false; m_increaseQuadAlpha = true; m_coloredQuadAlpha = 0.0f; } stages.exposureCalc.render(); // Move exposure towards target static const f32 EXPOSURE_STEP = 0.005f; stepTowards(soaOptions.get(OPT_HDR_EXPOSURE).value.f, stages.exposureCalc.getExposure(), EXPOSURE_STEP); // Post processing m_swapChain.reset(0, m_hdrTarget.getGeometryID(), m_hdrTarget.getGeometryTexture(0), soaOptions.get(OPT_MSAA).value.i > 0, false); // TODO(Ben): This is broken if (stages.ssao.isActive()) { stages.ssao.set(m_hdrTarget.getDepthTexture(), m_hdrTarget.getGeometryTexture(1), m_hdrTarget.getGeometryTexture(0), m_swapChain.getCurrent().getID()); stages.ssao.render(&m_voxelCamera); m_swapChain.swap(); m_swapChain.use(0, false); } // last effect should not swap swapChain if (stages.nightVision.isActive()) { stages.nightVision.render(); m_swapChain.swap(); m_swapChain.use(0, false); } if (stages.bloom.isActive()) { stages.bloom.render(); // Render star glow into same framebuffer for performance glBlendFunc(GL_ONE, GL_ONE); m_commonState->stages.spaceSystem.renderStarGlows(f32v3(1.0f)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_swapChain.swap(); m_swapChain.use(0, false); } else { glBlendFunc(GL_ONE, GL_ONE); m_commonState->stages.spaceSystem.renderStarGlows(f32v3(1.0f)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } // TODO: More Effects // Draw to backbuffer for the last effect // m_swapChain.bindPreviousTexture(0); m_swapChain.unuse(m_window->getWidth(), m_window->getHeight()); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); // TODO(Ben): Do we really need to clear depth here... glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); m_hdrTarget.bindDepthTexture(1); m_commonState->stages.hdr.render(); // UI // stages.devHud.render(); // stages.pda.render(); stages.pauseMenu.render(); // Cube face fade animation if (m_increaseQuadAlpha) { static const f32 FADE_INC = 0.07f; m_coloredQuadAlpha += FADE_INC; if (m_coloredQuadAlpha >= 3.5f) { m_coloredQuadAlpha = 3.5f; m_increaseQuadAlpha = false; } // m_coloredQuadRenderer.draw(m_commonState->quad, f32v4(0.0, 0.0, 0.0, vmath::min(m_coloredQuadAlpha, 1.0f))); } else if (m_coloredQuadAlpha > 0.0f) { static const float FADE_DEC = 0.01f; // m_coloredQuadRenderer.draw(m_commonState->quad, f32v4(0.0, 0.0, 0.0, vmath::min(m_coloredQuadAlpha, 1.0f))); m_coloredQuadAlpha -= FADE_DEC; } if (m_shouldScreenshot) dumpScreenshot(); // Check for errors, just in case checkGlError("GamePlayRenderer::render()"); } void GameplayRenderer::cycleDevHud(int offset VORB_UNUSED /* = 1 */) { // stages.devHud.cycleMode(offset); } void GameplayRenderer::toggleNightVision() { if (!stages.nightVision.isActive()) { stages.nightVision.setActive(true); m_nvIndex = 0; stages.nightVision.setParams(m_nvParams[m_nvIndex]); } else { m_nvIndex++; if (m_nvIndex >= m_nvParams.size()) { stages.nightVision.setActive(false); } else { stages.nightVision.setParams(m_nvParams[m_nvIndex]); } } } void GameplayRenderer::loadNightVision() { stages.nightVision.setActive(false); // TODO(Ben): This yaml code is outdated /*m_nvIndex = 0; m_nvParams.clear(); vio::IOManager iom; const cString nvData = iom.readFileToString("Data/NightVision.yml"); if (nvData) { Array arr; keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(nvData); keg::Node node = context.reader.getFirst(); keg::Value v = keg::Value::array(0, keg::Value::custom(0, "NightVisionRenderParams", false)); keg::evalData((ui8*)&arr, &v, node, context); for (size_t i = 0; i < arr.size(); i++) { m_nvParams.push_back(arr[i]); } context.reader.load(); delete[] nvData; } if (m_nvParams.size() < 1) { m_nvParams.push_back(NightVisionRenderParams::createDefault()); }*/ } void GameplayRenderer::toggleChunkGrid() { stages.chunkGrid.toggleActive(); } void GameplayRenderer::updateCameras() { const GameSystem* gs = m_state->gameSystem; const SpaceSystem* ss = m_state->spaceSystem; // Get the physics component if (m_renderState->hasVoxelPos) { m_voxelCamera.setFocalLength(0.0f); m_voxelCamera.setClippingPlane(0.1f, 10000.0f); m_voxelCamera.setPosition(m_renderState->playerPosition.gridPosition.pos + m_renderState->playerHead.relativePosition); m_voxelCamera.setOrientation(m_renderState->playerPosition.orientation * m_renderState->playerHead.relativeOrientation); m_voxelCamera.update(); } // Player is relative to a planet, so add position if needed CinematicCamera& spaceCamera = m_state->clientState.spaceCamera; // TODO(Ben): Shouldn't be touching ECS here. auto& phycmp = gs->physics.getFromEntity(m_state->clientState.playerEntity); auto& spcmp = gs->spacePosition.get(phycmp.spacePosition); if (spcmp.parentGravity) { auto it = m_renderState->spaceBodyPositions.find(spcmp.parentEntity); if (it != m_renderState->spaceBodyPositions.end()) { spaceCamera.setPosition(m_renderState->spaceCameraPos + it->second); } else { auto& gcmp = ss->sphericalGravity.get(spcmp.parentGravity); auto& npcmp = ss->namePosition.get(gcmp.namePositionComponent); spaceCamera.setPosition(m_renderState->spaceCameraPos + npcmp.position); } } else { spaceCamera.setPosition(m_renderState->spaceCameraPos); } spaceCamera.setIsDynamic(false); spaceCamera.setFocalLength(0.0f); spaceCamera.setClippingPlane(0.1f, 100000000000.0f); if (m_renderState->hasVoxelPos) { spaceCamera.setOrientation(m_renderState->spaceCameraOrientation * m_renderState->playerHead.relativeOrientation); } else { spaceCamera.setOrientation(m_renderState->spaceCameraOrientation); } spaceCamera.update(); } void GameplayRenderer::dumpScreenshot() { // Make screenshots directory vio::IOManager().makeDirectory("Screenshots"); // Take screenshot dumpFramebufferImage("Screenshots/", m_viewport); m_shouldScreenshot = false; } ================================================ FILE: SoA/GameplayRenderer.h ================================================ /// /// GameplayRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the render pipeline for the /// GamePlayScreen. /// #pragma once #ifndef GamePlayRenderer_h__ #define GamePlayRenderer_h__ #include #include #include #include #include "Camera.h" #include "ChunkGridRenderStage.h" #include "ChunkRenderer.h" #include "ColoredFullQuadRenderer.h" #include "CutoutVoxelRenderStage.h" #include "DevHudRenderStage.h" #include "GameRenderParams.h" #include "HdrRenderStage.h" #include "LiquidVoxelRenderStage.h" #include "NightVisionRenderStage.h" #include "OpaqueVoxelRenderStage.h" #include "PauseMenuRenderStage.h" #include "PdaRenderStage.h" #include "PhysicsBlockRenderStage.h" #include "SkyboxRenderStage.h" #include "SpaceSystemRenderStage.h" #include "SsaoRenderStage.h" #include "TransparentVoxelRenderStage.h" #include "BloomRenderStage.h" #include "ExposureCalcRenderStage.h" /// Forward declarations class App; class ChunkGridRenderStage; class ChunkMeshManager; class ChunkSlot; class CutoutVoxelRenderStage; class DevHudRenderStage; class GameSystem; class GameplayScreen; class HdrRenderStage; class LiquidVoxelRenderStage; class MeshManager; class NightVisionRenderStage; class OpaqueVoxelRenderStage; class PDA; class PauseMenu; class PauseMenuRenderStage; class PdaRenderStage; class PhysicsBlockRenderStage; class Player; class SkyboxRenderStage; class SpaceSystem; class SpaceSystemRenderStage; class TransparentVoxelRenderStage; class DebugRenderer; struct MTRenderState; struct CommonState; struct SoaState; class GameplayRenderer { public: /// Initializes the pipeline and passes dependencies void init(vui::GameWindow* window, StaticLoadContext& context, GameplayScreen* gameplayScreen, CommonState* commonState); /// Call this every frame before render void setRenderState(const MTRenderState* renderState); void hook(); void load(StaticLoadContext& context); void dispose(StaticLoadContext& context); void reloadShaders(); void updateGL(); /// Renders the pipeline. /// Make sure to call setRenderState first. virtual void render(); /// Cycles the dev hud /// @param offset: How much to offset the current mode void cycleDevHud(int offset = 1); /// Toggle the visibility of night vision void toggleNightVision(); /// Load night vision data void loadNightVision(); /// Toggle the visibility of chunkGrid void toggleChunkGrid(); void toggleWireframe() { m_wireframe = !m_wireframe; } void takeScreenshot() { m_shouldScreenshot = true; } volatile const bool& isLoaded() const { return m_isLoaded; } struct { OpaqueVoxelRenderStage opaqueVoxel; ///< Renders opaque voxels CutoutVoxelRenderStage cutoutVoxel; ///< Renders cutout voxels ChunkGridRenderStage chunkGrid; TransparentVoxelRenderStage transparentVoxel; ///< Renders transparent voxels LiquidVoxelRenderStage liquidVoxel; ///< Renders liquid voxels DevHudRenderStage devHud; ///< Renders the dev/debug HUD PdaRenderStage pda; ///< Renders the PDA PauseMenuRenderStage pauseMenu; ///< Renders the pause menu NightVisionRenderStage nightVision; ///< Renders night vision SSAORenderStage ssao; ///< Renders SSAO BloomRenderStage bloom; ///< Renders Bloom effect ExposureCalcRenderStage exposureCalc; ///< Calculate exposure } stages; DebugRenderer* debugRenderer = nullptr; private: void updateCameras(); void dumpScreenshot(); Camera m_voxelCamera; ChunkRenderer m_chunkRenderer; ColoredFullQuadRenderer m_coloredQuadRenderer; ///< For rendering full screen colored quads vg::GBuffer m_hdrTarget; ///< Framebuffer needed for the HDR rendering vg::RTSwapChain<2> m_swapChain; ///< Swap chain of framebuffers used for post-processing GameRenderParams m_gameRenderParams; ///< Shared rendering parameters for voxels GameplayScreen* m_gameplayScreen = nullptr; vui::GameWindow* m_window; CommonState* m_commonState = nullptr; SoaState* m_state = nullptr; ///< Game State vcore::RPCManager m_glrpc; std::thread* m_loadThread = nullptr; volatile bool m_isLoaded = false; // TODO: This is only for visualization purposes, must remove std::vector m_nvParams; ///< Different night vision styles ui32 m_nvIndex = 0; ui32v4 m_viewport; ///< Viewport to draw to ChunkMeshManager* m_meshManager; ///< Handle to the meshes const MTRenderState* m_renderState = nullptr; ///< The current MT render state float m_coloredQuadAlpha = 0.0f; bool m_increaseQuadAlpha = false; bool m_wireframe = false; bool m_shouldScreenshot = false; }; #endif // GamePlayRenderer_h__ ================================================ FILE: SoA/GasGiantComponentRenderer.cpp ================================================ #include "stdafx.h" #include "GasGiantComponentRenderer.h" #include "ShaderLoader.h" #include "SpaceSystemComponents.h" #include "RenderUtils.h" #include #include #include #include #include #include #define ICOSPHERE_SUBDIVISIONS 5 GasGiantComponentRenderer::~GasGiantComponentRenderer() { dispose(); } void GasGiantComponentRenderer::initGL() { if (!m_program.isCreated()) buildShader(); if (!m_vbo) buildMesh(); } void GasGiantComponentRenderer::draw(const GasGiantComponent& ggCmp, vecs::EntityID eid, const f32m4& VP, const f64q& orientation, const f32v3& relCamPos, const f32v3& lightDir, const float zCoef, const SpaceLightComponent* spCmp VORB_MAYBE_UNUSED, const AtmosphereComponent* aCmp) { // Get the render texture or load it if it hasn't been loaded // TODO(Ben): Use a renderable component instead VGTexture colorTexture = 0; auto it = m_colorTextures.find(eid); if (it == m_colorTextures.end()) { vg::ScopedBitmapResource b(vg::ImageIO().load(ggCmp.colorMapPath)); if (b.data) { colorTexture = vg::GpuMemory::uploadTexture(&b, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP); } else { fprintf(stderr, "Failed to load %s\n", ggCmp.colorMapPath.c_str()); } m_colorTextures[eid] = colorTexture; } else { colorTexture = it->second; } m_program.use(); // For logarithmic Z buffer glUniform1f(m_program.getUniform("unZCoef"), zCoef); // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)orientation.x; orientationF32.y = (f32)orientation.y; orientationF32.z = (f32)orientation.z; orientationF32.w = (f32)orientation.w; // Get rotated light direction const f32v3 rotLightDir = glm::inverse(orientationF32) * lightDir; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); // Set up matrix f32m4 WVP(1.0); setMatrixTranslation(WVP, -relCamPos); WVP = VP * WVP * glm::scale(f32v3(ggCmp.radius, ggCmp.radius * (1.0 - ggCmp.oblateness), ggCmp.radius)) * rotationMatrix; f32v3 rotRelCamPos = relCamPos * orientationF32; // Upload uniforms static f64 dt = 0.0; dt += 0.00000001; glUniform1f(unDT, (f32)dt); glUniformMatrix4fv(unWVP, 1, GL_FALSE, &WVP[0][0]); // Scattering uniforms f32 camHeight = glm::length(relCamPos); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &WVP[0][0]); glUniform3fv(m_program.getUniform("unCameraPos"), 1, &rotRelCamPos[0]); glUniform3fv(m_program.getUniform("unLightDirWorld"), 1, &rotLightDir[0]); glUniform3fv(m_program.getUniform("unInvWavelength"), 1, &aCmp->invWavelength4[0]); glUniform1f(m_program.getUniform("unCameraHeight2"), camHeight * camHeight); glUniform1f(m_program.getUniform("unOuterRadius"), aCmp->radius); glUniform1f(m_program.getUniform("unOuterRadius2"), aCmp->radius * aCmp->radius); glUniform1f(m_program.getUniform("unInnerRadius"), aCmp->planetRadius); glUniform1f(m_program.getUniform("unKrESun"), aCmp->kr * aCmp->esun); glUniform1f(m_program.getUniform("unKmESun"), aCmp->km * aCmp->esun); glUniform1f(m_program.getUniform("unKr4PI"), (f32)(aCmp->kr * M_4_PI)); glUniform1f(m_program.getUniform("unKm4PI"), (f32)(aCmp->km * M_4_PI)); float scale = 1.0f / (aCmp->radius - aCmp->planetRadius); glUniform1f(m_program.getUniform("unScale"), scale); glUniform1f(m_program.getUniform("unScaleDepth"), aCmp->scaleDepth); glUniform1f(m_program.getUniform("unScaleOverScaleDepth"), scale / aCmp->scaleDepth); glUniform1i(m_program.getUniform("unNumSamples"), 3); glUniform1f(m_program.getUniform("unNumSamplesF"), 3.0f); glUniform1f(m_program.getUniform("unG"), aCmp->g); glUniform1f(m_program.getUniform("unG2"), aCmp->g * aCmp->g); // Bind VAO glBindVertexArray(m_vao); // Bind lookup texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorTexture); glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, 0); glBindVertexArray(0); m_program.unuse(); } void GasGiantComponentRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); if (m_vbo) { vg::GpuMemory::freeBuffer(m_vbo); m_vbo = 0; } if (m_ibo) { vg::GpuMemory::freeBuffer(m_ibo); m_ibo = 0; } if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; } } void GasGiantComponentRenderer::buildShader() { m_program = ShaderLoader::createProgramFromFile("Shaders/GasGiant/GasGiant.vert", "Shaders/GasGiant/GasGiant.frag"); m_program.use(); glUniform1i(m_program.getUniform("unColorBandLookup"), 0); unWVP = m_program.getUniform("unWVP"); unDT = m_program.getUniform("unDT"); m_program.unuse(); } void GasGiantComponentRenderer::buildMesh() { std::vector indices; std::vector positions; // TODO(Ben): Optimize with LOD for far viewing vmesh::generateIcosphereMesh(ICOSPHERE_SUBDIVISIONS, indices, positions); m_numIndices = indices.size(); std::vector vertices(positions.size()); for (size_t i = 0; i < positions.size(); i++) { vertices[i].position = positions[i]; vertices[i].texCoord = (positions[i].y + 1.0f) / 2.0f; } glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); vg::GpuMemory::createBuffer(m_vbo); vg::GpuMemory::createBuffer(m_ibo); vg::GpuMemory::bindBuffer(m_vbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_vbo, vg::BufferTarget::ARRAY_BUFFER, vertices.size() * sizeof(GasGiantVertex), vertices.data(), vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(ui32), indices.data(), vg::BufferUsageHint::STATIC_DRAW); m_program.enableVertexAttribArrays(); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, GL_FALSE, sizeof(GasGiantVertex), offsetptr(GasGiantVertex, position)); glVertexAttribPointer(m_program.getAttribute("vTexCoord"), 1, GL_FLOAT, GL_FALSE, sizeof(GasGiantVertex), offsetptr(GasGiantVertex, texCoord)); glBindVertexArray(0); } ================================================ FILE: SoA/GasGiantComponentRenderer.h ================================================ /// /// GasGiantComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 3 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders gas giant components /// #pragma once #ifndef GasGiantComponentRenderer_h__ #define GasGiantComponentRenderer_h__ #include #include #include #include #include struct GasGiantComponent; struct SpaceLightComponent; struct AtmosphereComponent; struct GasGiantVertex { f32v3 position; f32 texCoord; }; class GasGiantComponentRenderer { public: ~GasGiantComponentRenderer(); void initGL(); void draw(const GasGiantComponent& ggCmp, vecs::EntityID eid, const f32m4& VP, const f64q& orientation, const f32v3& relCamPos, const f32v3& lightDir, const float zCoef, const SpaceLightComponent* spCmp, const AtmosphereComponent* aCmp); void dispose(); private: void buildShader(); void buildMesh(); vg::GLProgram m_program; VGBuffer m_vbo = 0; VGIndexBuffer m_ibo = 0; VGVertexArray m_vao = 0; int m_numIndices = 0; // TODO(Ben): Use a renderable component instead std::unordered_map m_colorTextures; // TODO(Ben): UBO VGUniform unWVP; VGUniform unDT; }; #endif // GasGiantComponentRenderer_h__ ================================================ FILE: SoA/GenerateTask.cpp ================================================ #include "stdafx.h" #include "GenerateTask.h" #include "Chunk.h" #include "ChunkGenerator.h" #include "ChunkGrid.h" #include "FloraGenerator.h" void GenerateTask::execute(WorkerData* workerData) { Chunk& chunk = query->chunk; // Check if this is a heightmap gen if (chunk.gridData->isLoading) { chunkGenerator->m_proceduralGenerator.generateHeightmap(&chunk, heightData); } else { // Its a chunk gen switch (query->genLevel) { case ChunkGenLevel::GEN_DONE: case ChunkGenLevel::GEN_TERRAIN: chunkGenerator->m_proceduralGenerator.generateChunk(&chunk, heightData); chunk.genLevel = GEN_TERRAIN; // TODO(Ben): Not lazy load. if (!workerData->floraGenerator) { workerData->floraGenerator = new FloraGenerator; } generateFlora(workerData, chunk); chunk.genLevel = ChunkGenLevel::GEN_DONE; break; case ChunkGenLevel::GEN_FLORA: chunk.genLevel = ChunkGenLevel::GEN_DONE; break; case ChunkGenLevel::GEN_SCRIPT: chunk.genLevel = ChunkGenLevel::GEN_DONE; break; default: break; } query->m_isFinished = true; query->m_cond.notify_one(); // TODO(Ben): Not true for all gen? chunk.isAccessible = true; } chunkGenerator->finishQuery(query); } struct ChunkFloraArrays { std::vector fNodes; std::vector wNodes; }; void GenerateTask::generateFlora(WorkerData* workerData, Chunk& chunk) { std::vector fNodes, wNodes; workerData->floraGenerator->generateChunkFlora(&chunk, heightData, fNodes, wNodes); // Sort based on chunk to minimize locking std::map chunkMap; // Add all nodes // TODO(Ben): There should be a way to approximate size needs or use map on generator side. for (auto& it : fNodes) { ChunkID id(chunk.getID()); id.x += FloraGenerator::getChunkXOffset(it.chunkOffset); id.y += FloraGenerator::getChunkYOffset(it.chunkOffset); id.z += FloraGenerator::getChunkZOffset(it.chunkOffset); chunkMap[id].fNodes.emplace_back(it.blockID, it.blockIndex); } for (auto& it : wNodes) { ChunkID id(chunk.getID()); id.x += FloraGenerator::getChunkXOffset(it.chunkOffset); id.y += FloraGenerator::getChunkYOffset(it.chunkOffset); id.z += FloraGenerator::getChunkZOffset(it.chunkOffset); chunkMap[id].wNodes.emplace_back(it.blockID, it.blockIndex); } // Traverse chunks for (auto& it : chunkMap) { ChunkHandle h = query->grid->accessor.acquire(it.first); // TODO(Ben): Handle other case if (h->genLevel >= GEN_TERRAIN) { { std::lock_guard l(h->dataMutex); for (auto& node : it.second.wNodes) { h->blocks.set(node.blockIndex, node.blockID); } for (auto& node : it.second.fNodes) { if (h->blocks.get(node.blockIndex) == 0) { h->blocks.set(node.blockIndex, node.blockID); } } } if (h->genLevel == GEN_DONE) h->DataChange(h); } else { query->grid->nodeSetter.setNodes(h, GEN_TERRAIN, it.second.wNodes, it.second.fNodes); } h.release(); } std::vector().swap(chunk.floraToGenerate); } ================================================ FILE: SoA/GenerateTask.h ================================================ /// /// GenerateTask.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Implements the generate task for SoA chunk generation /// #pragma once #ifndef LoadTask_h__ #define LoadTask_h__ #include #include "VoxPool.h" class Chunk; class ChunkGenerator; class ChunkQuery; struct PlanetHeightData; #define GENERATE_TASK_ID 1 // Represents A Chunk Load Task class GenerateTask : public vcore::IThreadPoolTask { public: GenerateTask() : vcore::IThreadPoolTask(GENERATE_TASK_ID) {} void init(ChunkQuery *query, PlanetHeightData* heightData, ChunkGenerator* chunkGenerator) { this->query = query; this->heightData = heightData; this->chunkGenerator = chunkGenerator; } void execute(WorkerData* workerData) override; // Chunk To Be Loaded ChunkQuery* query; ChunkGenerator* chunkGenerator; ///< send finished query here // Loading Information PlanetHeightData* heightData; private: void generateFlora(WorkerData* workerData, Chunk& chunk); }; #endif // LoadTask_h__ ================================================ FILE: SoA/GeometrySorter.cpp ================================================ #include "stdafx.h" #include "GeometrySorter.h" #include "soaUtils.h" #include #include "ChunkRenderer.h" std::vector GeometrySorter::_distBuffer; i32 convertData(Distanceclass* data) { return data->distance; } bool comparator(const Distanceclass& i, const Distanceclass& j) { return (i.distance > j.distance); } void GeometrySorter::sortTransparentBlocks(ChunkMesh* cm, const i32v3& cameraPos) { _distBuffer.resize(cm->transQuadPositions.size()); for (size_t i = 0; i < cm->transQuadPositions.size(); i++) { _distBuffer[i].quadIndex = i; //We multiply by 2 because we need twice the precision of integers per block //we subtract by 1 in order to ensure that the camera position is centered on a block _distBuffer[i].distance = selfDot(((i32v3(cm->position) - cameraPos) << 1) - 1 + i32v3(cm->transQuadPositions[i])); } // radixSort(&(_distBuffer[0]), _distBuffer.size(), convertData, 31); std::sort(_distBuffer.begin(), _distBuffer.end(), comparator); int startIndex; int j = 0; for (size_t i = 0; i < _distBuffer.size(); i++) { startIndex = _distBuffer[i].quadIndex * 4; cm->transQuadIndices[j] = startIndex; cm->transQuadIndices[j + 1] = startIndex + 1; cm->transQuadIndices[j + 2] = startIndex + 2; cm->transQuadIndices[j + 3] = startIndex + 2; cm->transQuadIndices[j + 4] = startIndex + 3; cm->transQuadIndices[j + 5] = startIndex; j += 6; } } ================================================ FILE: SoA/GeometrySorter.h ================================================ #pragma once #include #include "Vorb/types.h" class ChunkMesh; class Distanceclass { public: i32 quadIndex; i32 distance; }; class GeometrySorter { public: static void sortTransparentBlocks(ChunkMesh* cm, const i32v3& cameraPos); private: static std::vector _distBuffer; }; ================================================ FILE: SoA/HdrRenderStage.cpp ================================================ #include "stdafx.h" #include "HdrRenderStage.h" #include #include "Camera.h" #include "Chunk.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "SoaOptions.h" #include "RenderUtils.h" #include "ShaderLoader.h" void HdrRenderStage::hook(vg::FullQuadVBO* quad) { m_quad = quad; } void HdrRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { if (m_programBlur.isCreated()) m_programBlur.dispose(); if (m_programDoFBlur.isCreated()) m_programDoFBlur.dispose(); } void HdrRenderStage::render(const Camera* camera /*= nullptr*/) { f32m4 oldVP = m_oldVP; f32m4 vp; if (camera) { vp = camera->getProjectionMatrix() * camera->getViewMatrix(); m_oldVP = vp; } vg::GLProgram* program; if (soaOptions.get(OPT_MOTION_BLUR).value.i > 0) { if (!m_programBlur.isCreated()) { m_programBlur = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/MotionBlur.frag", nullptr, "#define MOTION_BLUR\n"); } program = &m_programBlur; } else { if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/MotionBlur.frag"); } program = &m_program; } // TODO(Ben): DOF shader? //program = /*graphicsOptions.depthOfField == 1 ? _glProgramDoFBlur :*/ _glProgram; program->use(); program->enableVertexAttribArrays(); glUniform1i(program->getUniform("unTex"), 0); glUniform1f(program->getUniform("unGamma"), 1.0f / soaOptions.get(OPT_GAMMA).value.f); glUniform1f(program->getUniform("unExposure"), soaOptions.get(OPT_HDR_EXPOSURE).value.f); if (soaOptions.get(OPT_MOTION_BLUR).value.i > 0) { f32m4 newInverseVP = glm::inverse(vp); glUniform1i(program->getUniform("unTexDepth"), 1); glUniformMatrix4fv(program->getUniform("unVPPrev"), 1, GL_FALSE, &oldVP[0][0]); glUniformMatrix4fv(program->getUniform("unVPInv"), 1, GL_FALSE, &newInverseVP[0][0]); glUniform1i(program->getUniform("unNumSamples"), soaOptions.get(OPT_MOTION_BLUR).value.i); glUniform1f(program->getUniform("unBlurIntensity"), 0.5f); } //if (graphicsOptions.depthOfField > 0) { // glUniform1f(_glprogram->getUniform("unFocalLen"), 70.0f); // glUniform1f(_glprogram->getUniform("unZfocus"), 0.96f); // [0, 1] //} glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); program->disableVertexAttribArrays(); program->unuse(); } ================================================ FILE: SoA/HdrRenderStage.h ================================================ /// /// HdrRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the HDR render stage, which /// does HDR post processing. /// #pragma once #ifndef HdrRenderStage_h__ #define HdrRenderStage_h__ #include #include #include #include "IRenderStage.h" class Camera; class HdrRenderStage : public IRenderStage { public: /// @param quad: Quad used for rendering to screen void hook(vg::FullQuadVBO* quad); /// Disposes and deletes the shader and turns off visibility /// If stage does lazy init, shader will reload at next draw virtual void dispose(StaticLoadContext& context) override; /// Draws the render stage virtual void render(const Camera* camera = nullptr) override; private: vg::GLProgram m_program; vg::GLProgram m_programBlur; ///< Motion blur enabled vg::GLProgram m_programDoFBlur; ///< Motion blur and DoF enabled vg::FullQuadVBO* m_quad = nullptr; ///< For use in processing through data f32m4 m_oldVP = f32m4(1.0f); ///< ViewProjection of previous frame }; #endif // HdrRenderStage_h__ ================================================ FILE: SoA/HeadComponentUpdater.cpp ================================================ #include "stdafx.h" #include "HeadComponentUpdater.h" #include "Constants.h" #include "GameSystem.h" #include "soaUtils.h" void HeadComponentUpdater::update(GameSystem* gameSystem VORB_UNUSED) { // Empty for now } void HeadComponentUpdater::rotateFromMouse(GameSystem* gameSystem, vecs::ComponentID cmpID, float dx, float dy, float speed) { // Seems like a race condition auto& cmp = gameSystem->head.get(cmpID); // Pitch cmp.eulerAngles.x += dy * speed; cmp.eulerAngles.x = vmath::clamp(cmp.eulerAngles.x, -M_PI_2, M_PI_2); // Yaw cmp.eulerAngles.y += dx * speed; // Check if we need to rotate the body if (cmp.eulerAngles.y < -M_PI_2) { auto& vpCmp = gameSystem->voxelPosition.get(cmp.voxelPosition); vpCmp.eulerAngles.y += cmp.eulerAngles.y + M_PI_2; cmp.eulerAngles.y = -M_PI_2; } else if (cmp.eulerAngles.y > M_PI_2) { auto& vpCmp = gameSystem->voxelPosition.get(cmp.voxelPosition); vpCmp.eulerAngles.y += cmp.eulerAngles.y - M_PI_2; cmp.eulerAngles.y = M_PI_2; } cmp.relativeOrientation = f64q(cmp.eulerAngles); } ================================================ FILE: SoA/HeadComponentUpdater.h ================================================ // // HeadComponentUpdater.h // Seed of Andromeda // // Created by Benjamin Arnold on 15 Aug 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Updater for Head Components. // #pragma once #ifndef HeadComponentUpdater_h__ #define HeadComponentUpdater_h__ #include class GameSystem; class HeadComponentUpdater { public: void update(GameSystem* gameSystem); static void rotateFromMouse(GameSystem* gameSystem, vecs::ComponentID cmpID, float dx, float dy, float speed); }; #endif // HeadComponentUpdater_h__ ================================================ FILE: SoA/IRenderStage.h ================================================ /// /// IRenderStage.h /// Seed of Andromeda /// /// Created by Ben Arnold on 28 Oct 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides an abstract interface for a render /// stage. /// #pragma once #ifndef IRenderStage_h_ #define IRenderStage_h_ #include #include class Camera; class StaticLoadContext; struct SoaState; DECL_VUI(class GameWindow) class IRenderStage { public: /*! @brief Simple fast initialization performed in main game loop. * * Should increment the amount of expected work to be done in the context. * Only simple initialization logic should be performed here * * @param context: Common loading context. */ virtual void init(vui::GameWindow* window, StaticLoadContext& context VORB_MAYBE_UNUSED) { m_window = window; } /*! @brief Invokes core loading logic * * The loading logic of this render stage is invoked on a separate thread. The work * completed should be updated on the context as it is finished. * * @param context: Common loading context that holds an RPCManager */ virtual void load(StaticLoadContext& context VORB_MAYBE_UNUSED) {} /*! @brief Destroys all resources held by this render stage. * * Called within the game loop, the destruction should be fast and minimal. * * @param context: Common loading context. */ virtual void dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) {} /*! @brief Implementation-defined rendering logic. * * The rendering code should only concern itself with setting up the shaders, geometry * and sending the draw calls. Other state (like draw destinations) are handled externally. * * @param camera: Viewpoint for the rendering */ virtual void render(const Camera* camera) = 0; virtual const volatile bool& isBuilt() const { return m_built; } /// Sets the visibility of the stage /// @param isVisible: The visibility virtual void setActive(bool isVisible) { m_isActive = isVisible; } /// Toggles the isVisible field virtual void toggleActive() { m_isActive = !m_isActive; } /// Check if the stage is visible virtual const bool& isActive() const { return m_isActive; } protected: vui::GameWindow* m_window; bool m_isActive = true; ///< Determines if the stage should be rendered volatile bool m_built = false; }; #endif // IRenderStage_h_ ================================================ FILE: SoA/ImageAssetLoader.cpp ================================================ #include "stdafx.h" #include "ImageAssetLoader.h" #include void vcore::AssetBuilder::create(const vpath& p, OUT ImageAsset* asset, vcore::RPCManager& rpc) { vg::BitmapResource bmp = m_imageLoader.load(p, vg::ImageIOFormat::RGBA_UI8); if (!bmp.data) return; asset->texture.width = bmp.width; asset->texture.height = bmp.height; GLRPC so(asset); so.set([bmp](Sender, void* userData) { ImageAsset* asset = (ImageAsset*)userData; // Create the texture glGenTextures(1, &asset->texture.id); glBindTexture(GL_TEXTURE_2D, asset->texture.id); vg::SamplerState::LINEAR_WRAP.set(GL_TEXTURE_2D); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, asset->texture.width, asset->texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data); glBindTexture(GL_TEXTURE_2D, 0); }); rpc.invoke(&so); vg::ImageIO::free(bmp); } void vcore::AssetBuilder::destroy(ImageAsset* asset) { glDeleteTextures(1, &asset->texture.id); } ================================================ FILE: SoA/ImageAssetLoader.h ================================================ /// /// ImageAssetLoader.h /// /// Created by Cristian Zaloj on 13 Feb 2015 /// /// Summary: /// Loads image assets. /// #pragma once #ifndef ImageAssetLoader_h__ #define ImageAssetLoader_h__ #include #include #include class ImageAsset : public vcore::Asset { public: vg::Texture texture; }; template<> struct vcore::AssetBuilder { public: void create(const vpath& p, OUT ImageAsset* asset, vcore::RPCManager& rpc); void destroy(ImageAsset* asset); private: vg::ImageIO m_imageLoader; }; CONTEXTUAL_ASSET_LOADER(ImageAssetLoader, ImageAsset); #endif // ImageAssetLoader_h__ ================================================ FILE: SoA/IniParser.cpp ================================================ #include #include #include #include //stupid Windows SDK mucking up the namespace #if defined(_WIN32) || defined(_WIN64) #define UNKNOWN WINDOWS_UNKNOWN #define CHAR WINDOWS_CHAR #define SHORT WINDOWS_SHORT #define INT WINDOWS_INT #define LONG WINDOWS_LONG #define FLOAT WINDOWS_FLOAT #define DOUBLE WINDOWS_DOUBLE #endif #include "IniParser.h" #if defined(_WIN32) || defined(_WIN64) #undef UNKNOWN #undef CHAR #undef SHORT #undef INT #undef LONG #undef FLOAT #undef DOUBLE #endif // These Are All The Types That Can Be Parsed enum PTYPE { UNKNOWN, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, STRING }; // These Are Used In The File To Denote Type #define CHAR_BYTE 'b' #define CHAR_SHORT 'h' #define CHAR_INT 'i' #define CHAR_LONG 'l' #define CHAR_FLOAT 'f' #define CHAR_DOUBLE 'd' #define CHAR_STRING 's' long GetFileSize(const char* filename) { // Check The File Path if(!filename) return -1; // Use File Information For The Size struct stat stat_buf; int rc = stat(filename, &stat_buf); return rc == 0 ? stat_buf.st_size : -1; } // Helpers For Navigating Within The String inline char* GoToNonWhitespaceML(char* s) { // Ensure Argument if(!s) return 0; while(true) { switch(*s) { // All Whitespace Is Skipped case ' ': case '\t': case '\r': case '\n': s++; break; default: return s; } } } inline char* GoToNonWhitespace(char* s) { // Ensure Argument if(!s) return 0; while(true) { switch(*s) { // Only Spaces And Tabs Count As Whitespace case ' ': case '\t': s++; break; default: return s; } } } // Custom Numerical Converters For Integers unsigned long int ExtractHex(char* s) { // Ensure Argument if(!s) return 0; // Get Rid Of Beginning Whitespace s = GoToNonWhitespace(s); // Check For Inverting Values bool flip = false; if(*s == '~') { flip = true; s++; } // Parse The Value int i = 0; unsigned long int num = 0; while(true) { switch(s[i]) { case 0: case ' ': case '\t': case '\r': case '\n': // The End Of The String Has Been Reached return flip ? ~num : num; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // Append Values In Hex Form num <<= 4; num |= s[i] - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': // Append Values In Hex Form num <<= 4; num |= s[i] - 'a' + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': // Append Values In Hex Form num <<= 4; num |= s[i] - 'A' + 10; break; default: // Other Characters Are Unsupported #ifdef DEBUG printf("Found Unsupported Character While Parsing Hex Value\n"); #endif // DEBUG return flip ? ~num : num; } // Move To The Next Character i++; } } unsigned long int ExtractOctal(char* s) { // Ensure Argument if(!s) return 0; // Get Rid Of Beginning Whitespace s = GoToNonWhitespace(s); // Check For Hex Value if(s[0] == 'x') return ExtractHex(s + 1); // Check For Inverting Values bool flip = false; if(*s == '~') { flip = true; s++; } // Parse The Value int i = 0; unsigned long int num = 0; while(true) { switch(s[i]) { case 0: case ' ': case '\t': case '\r': case '\n': // The End Of The String Has Been Reached return flip ? ~num : num; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': // Append Values In Octal Form num <<= 3; num |= s[i] - '0'; break; default: // Other Characters Are Unsupported #ifdef DEBUG printf("Found Unsupported Character While Parsing Hex Value\n"); #endif // DEBUG return flip ? ~num : num; } // Move To The Next Character i++; } } unsigned long int ExtractNumber(char* s) { // Ensure Argument if(!s) return 0; // Get Rid Of Beginning Whitespace s = GoToNonWhitespace(s); // Check For Octal Value if(s[0] == '0') return ExtractOctal(s + 1); // Check For Inverting Values bool flip = false; if(*s == '-') { flip = true; s++; } // Parse The Value int i = 0; unsigned long int num = 0; while(true) { switch(s[i]) { case 0: case ' ': case '\t': case '\r': case '\n': // The End Of The String Has Been Reached return flip ? -num : num; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // Append Values In Octal Form num *= 10; num += s[i] - '0'; break; default: // Other Characters Are Unsupported #ifdef DEBUG printf("Found Unsupported Character While Parsing Hex Value\n"); #endif // DEBUG return flip ? ~num : num; } // Move To The Next Character i++; } } // Helpers To Extract Information From An INI Value PTYPE ExtractType(char*& s) { // Ensure Argument if(!s) return UNKNOWN; // Go To The First Character In The Type s = GoToNonWhitespaceML(s); if(*s == 0) return UNKNOWN; // Transfer Chars To An Enum switch(*s) { case CHAR_BYTE: return CHAR; case CHAR_SHORT: return SHORT; case CHAR_INT: return INT; case CHAR_LONG: return LONG; case CHAR_FLOAT: return FLOAT; case CHAR_DOUBLE: return DOUBLE; case CHAR_STRING: return STRING; default: return UNKNOWN; } } char* ExtractValue(char*& s) { int cap = 10, count = 0; char* v = (char*)malloc(cap); while(*s != '}' && *s != 0) { // Check For Escape if(*s == '\\') { s++; if(*s == 0) break; } // Append The Character v[count++] = *s; s++; // Check For String Capacity if(count == cap) { cap <<= 1; v = (char*)realloc(v, cap); } } // Check For A Bad Value if(*s == 0) { free(v); return 0; } else s++; // Null-terminated v[count] = 0; return v; } // This Actually Parses The File int ByteBlit(const char* file, void* dst, int maxSize) { // Keep Track Of The Current Number Of Bytes Read int curSize = 0; char* bytes = (char*)dst; // Attempt To Open The File FILE* f; f = fopen(file, "r"); if(!f) { #ifdef DEBUG printf("Could Not Open INI File\nError: %d", ferror(f)); #endif // DEBUG throw; } long fs = GetFileSize(file); char* data = (char*)malloc(fs); fread(data, fs, 1, f); while(curSize < maxSize) { // Extract The Type Of The Data PTYPE pt = ExtractType(data); if(pt == PTYPE::UNKNOWN) break; // Move To The First Bracket while(*data != '{' && *data != 0) data++; if(*data == 0) break; data++; // Extract The Value Into A New String char* value = ExtractValue(data); if(!value) break; #define APPENDER(TYPE, SIZE, FUNC) \ if(curSize + 1 <= maxSize) { \ TYPE val = (TYPE)FUNC(value); \ memcpy(bytes + curSize, &val, SIZE); \ curSize += SIZE; \ } \ else maxSize = -1; \ break // Copy Value Data Into The Destination Making Sure To Not Go Over Alotted Size switch(pt) { case PTYPE::CHAR: APPENDER(unsigned char, 1, ExtractNumber); case PTYPE::SHORT: APPENDER(unsigned short, 2, ExtractNumber); case PTYPE::INT: APPENDER(unsigned int, 4, ExtractNumber); case PTYPE::LONG: APPENDER(unsigned long int, 8, ExtractNumber); case PTYPE::FLOAT: APPENDER(float, 4, atof); case PTYPE::DOUBLE: APPENDER(double, 8, atof); case PTYPE::STRING: APPENDER(char*, sizeof(char*), ); default: break; } } // Return Amount Of Data That Was Read return curSize; } // A Test Case #ifdef TEST_INIPARSER struct MYSTR { public: int V1; int V2; int V3; int V4; char* VStr; }; int test_main(int argc, char** argv) { MYSTR v; int bytes = ByteBlit("src/test/data.ini", &v, sizeof(MYSTR)); printf(v.VStr); return 0; } #endif // TEST_INIPARSER ================================================ FILE: SoA/IniParser.h ================================================ #pragma once #include "Vorb/types.h" // Parses A File Into A Block Of Data /* * Format: * type MiscCharacters{data} * Example: * c - The ASCII A - {0x41} */ i32 ByteBlit(const cString file, void* dst, i32 maxSize); ================================================ FILE: SoA/InitScreen.cpp ================================================ #include "stdafx.h" #include "InitScreen.h" #include #include #include #include #include #include "App.h" #include "MainMenuLoadScreen.h" #include "GameManager.h" #define INIT_SCREEN_FONT "Fonts/orbitron_bold-webfont.ttf" #define INIT_SCREEN_FONT_SIZE 32 #define COLOR_FAILURE Red #define COLOR_SUCCESS LimeGreen i32 InitScreen::getNextScreen() const { return m_app->scrLoad->getIndex(); } i32 InitScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void InitScreen::build() { // Empty } void InitScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void InitScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { buildSpriteResources(); checkRequirements(); // Background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0); } void InitScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { destroySpriteResources(); } void InitScreen::update(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // Immediately move to next state m_state = vui::ScreenState::CHANGE_NEXT; } void InitScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { const vui::GameWindow* w = &m_game->getWindow(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_sb->render(f32v2(w->getWidth(), w->getHeight()), &vg::SamplerState::LINEAR_WRAP, &vg::DepthState::FULL, &vg::RasterizerState::CULL_NONE); } void InitScreen::buildSpriteResources() { m_sb = new vg::SpriteBatch(true, true); m_font = new vg::SpriteFont(); m_font->init(INIT_SCREEN_FONT, INIT_SCREEN_FONT_SIZE); } void InitScreen::destroySpriteResources() { m_sb->dispose(); delete m_sb; m_sb = nullptr; m_font->dispose(); delete m_font; m_font = nullptr; } void InitScreen::checkRequirements() { static const f32 textSize = 30.0f; static const f32 textOffset = 5.0f; m_canContinue = true; const vui::GameWindow* w = &m_game->getWindow(); f32v2 pos(0, 0); f32v2 rectSize(w->getWidth(), textSize + textOffset * 2.0f); f32v2 textOff(textOffset, textOffset); // Check If Application Can Proceed #define INIT_BRANCH(MESSAGE) { \ m_sb->draw(0, pos, rectSize, color::COLOR_FAILURE, 0.5f); \ m_sb->drawString(m_font, MESSAGE, pos + textOff, textSize, 1.0f, color::White); \ m_canContinue = false; \ } else { \ m_sb->draw(0, pos, rectSize, color::COLOR_SUCCESS, 0.5f); \ m_sb->drawString(m_font, MESSAGE, pos + textOff, textSize, 1.0f, color::White); \ } \ pos.y += rectSize.y; const vg::GraphicsDeviceProperties& gdProps = vg::GraphicsDevice::getCurrent()->getProperties(); m_sb->begin(); if (gdProps.glVersionMajor < 3) INIT_BRANCH("OpenGL Version"); if (!GLEW_VERSION_2_1) INIT_BRANCH("GLEW 2.1"); if (gdProps.maxTextureUnits < 8) INIT_BRANCH("Texture Units"); // Inform User What Will Happen pos.y += textSize * 0.5f; if (m_canContinue) { m_sb->draw(0, pos, rectSize, color::COLOR_SUCCESS, 0.5f); m_sb->drawString(m_font, "Application Will Proceed", pos + textOff, textSize, 1.0f, color::White); } else { m_sb->draw(0, pos, rectSize, color::COLOR_FAILURE, 0.5f); m_sb->drawString(m_font, "Application Will Now Exit", pos + textOff, textSize, 1.0f, color::White); } m_sb->drawString(m_font, "Press Any Key To Continue", f32v2(10.0f, w->getHeight() - 30.0f), 24.0f, 1.0f, color::LightGray); m_sb->end(vg::SpriteSortMode::TEXTURE); #ifdef DEBUG printf("System Met Minimum Requirements\n"); #endif // DEBUG } ================================================ FILE: SoA/InitScreen.h ================================================ #pragma once #include #include class App; DECL_VG(class SpriteBatch; class SpriteFont); class InitScreen : public vui::IAppScreen { public: CTOR_APP_SCREEN_INL(InitScreen, App) { } virtual i32 getNextScreen() const; virtual i32 getPreviousScreen() const; virtual void build(); virtual void destroy(const vui::GameTime& gameTime); virtual void onEntry(const vui::GameTime& gameTime); virtual void onExit(const vui::GameTime& gameTime); virtual void update(const vui::GameTime& gameTime); virtual void draw(const vui::GameTime& gameTime); private: void buildSpriteResources(); void destroySpriteResources(); // Check Requirements And Draws Results void checkRequirements(); vg::SpriteBatch* m_sb; vg::SpriteFont* m_font; bool m_canContinue; }; ================================================ FILE: SoA/InputMapper.cpp ================================================ #include "stdafx.h" #include "InputMapper.h" #include #include #include "GameManager.h" #include "Inputs.h" #include "VirtualKeyKegDef.inl" struct InputKegArray { Array defaultKey; Array key; }; KEG_TYPE_DECL(InputKegArray); KEG_TYPE_DEF(InputKegArray, InputKegArray, kt) { using namespace keg; kt.addValue("defaultKey", Value::array(offsetof(InputKegArray, defaultKey), Value::custom(0, "VirtualKey", true))); kt.addValue("key", Value::array(offsetof(InputKegArray, key), Value::custom(0, "VirtualKey", true))); } InputMapper::InputMapper() { memset(m_keyStates, 0, sizeof(m_keyStates)); } InputMapper::~InputMapper() { stopInput(); } bool InputMapper::getInputState(const InputID id) { // Check Input if (id < 0 || id >= (int)m_inputs.size()) return false; return m_keyStates[m_inputs.at(id).key]; } InputMapper::InputID InputMapper::createInput(const nString& inputName, VirtualKey defaultKey) { InputID id = getInputID(inputName); if (id >= 0) return id; id = m_inputs.size(); m_inputLookup[inputName] = id; m_inputs.emplace_back(id, inputName, defaultKey, this); m_keyCodeMap[m_inputs.back().key].push_back(id); return id; } InputMapper::InputID InputMapper::getInputID(const nString& inputName) const { auto iter = m_inputLookup.find(inputName); if (iter != m_inputLookup.end()) { return iter->second; } else { return -1; } } void InputMapper::loadInputs(const nString &location /* = INPUTMAPPER_DEFAULT_CONFIG_LOCATION */) { vio::IOManager iom; //TODO PASS IN nString data; // If the file doesn't exist, just make it with defaults if (!iom.fileExists(location)) { saveInputs(location); return; } iom.readFileToString(location.c_str(), data); if (data.length() == 0) { fprintf(stderr, "Failed to load %s", location.c_str()); throw 33; } keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { perror(location.c_str()); context.reader.dispose(); throw 34; } // Manually parse yml file auto f = makeFunctor([&] (Sender, const nString& name, keg::Node value) { InputKegArray kegArray; keg::parse((ui8*)&kegArray, value, context, &KEG_GET_TYPE(InputKegArray)); // TODO(Ben): Somehow do multikey support // Right now its only using the first key InputID id = m_inputs.size(); m_inputs.emplace_back(id, name, kegArray.defaultKey[0], this); m_keyCodeMap[m_inputs.back().key].push_back(id); if (kegArray.key.size()) { m_inputs.back().key = kegArray.key[0]; } else { m_inputs.back().key = kegArray.defaultKey[0]; } m_inputLookup[name] = id; }); context.reader.forAllInMap(node, &f); context.reader.dispose(); } void InputMapper::startInput() { if (!m_receivingInput) { vui::InputDispatcher::mouse.onButtonDown += makeDelegate(this, &InputMapper::onMouseButtonDown); vui::InputDispatcher::mouse.onButtonUp += makeDelegate(this, &InputMapper::onMouseButtonDown); vui::InputDispatcher::key.onKeyDown += makeDelegate(this, &InputMapper::onKeyDown); vui::InputDispatcher::key.onKeyUp += makeDelegate(this, &InputMapper::onKeyUp); m_receivingInput = true; } } void InputMapper::stopInput() { if (m_receivingInput) { vui::InputDispatcher::mouse.onButtonDown -= makeDelegate(this, &InputMapper::onMouseButtonDown); vui::InputDispatcher::mouse.onButtonUp -= makeDelegate(this, &InputMapper::onMouseButtonDown); vui::InputDispatcher::key.onKeyDown -= makeDelegate(this, &InputMapper::onKeyDown); vui::InputDispatcher::key.onKeyUp -= makeDelegate(this, &InputMapper::onKeyUp); m_receivingInput = false; } } void InputMapper::saveInputs(const nString &filePath VORB_UNUSED /* = INPUTMAPPER_DEFAULT_CONFIG_LOCATION */) { //TODO(Ben): Implement and remove VORB_UNUSED tag. // vio::IOManager iom; // Just build the data string manually then write it /* bool tmp; keg::Enum enm; nString data = ""; for (auto& input : m_inputs) { data += input->name + ":\n"; data += " defaultKey:\n"; data += " - " + keg::getEnum(tmp, .getValue() }*/ } VirtualKey InputMapper::getKey(const InputID id) { if (id < 0 || id >= (int)m_inputs.size()) return VKEY_HIGHEST_VALUE; return m_inputs.at(id).key; } void InputMapper::setKey(const InputID id, VirtualKey key) { // Need to remove old key state VirtualKey oldKey = m_inputs.at(id).key; auto it = m_keyCodeMap.find(oldKey); auto& vec = it->second; for (size_t i = 0; i < vec.size(); i++) { // Remove the input from the vector keyed on VirtualKey if (vec[i] == id) { vec[i] = vec.back(); vec.pop_back(); break; } } // Set new key m_keyCodeMap[key].push_back(id); m_inputs[id].key = key; } void InputMapper::setKeyToDefault(const InputID id) { if (id < 0 || id >= (int)m_inputs.size()) return; setKey(id, m_inputs.at(id).defaultKey); } void InputMapper::onMouseButtonDown(Sender, const vui::MouseButtonEvent& e) { ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; if (!m_keyStates[code]) { m_keyStates[code] = true; // TODO(Ben): input mapping for mouse //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); //if (it != m_keyCodeMap.end()) { // // Call all events mapped to that virtual key // for (auto& id : it->second) { // m_inputs[id].downEvent(e.keyCode); // } //} } } void InputMapper::onMouseButtonUp(Sender, const vui::MouseButtonEvent& e) { ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; m_keyStates[code] = false; // TODO(Ben): input mapping for mouse //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); //if (it != m_keyCodeMap.end()) { // // Call all events mapped to that virtual key // for (auto& id : it->second) { // m_inputs[id].upEvent(e.keyCode); // } //} } void InputMapper::onKeyDown(Sender, const vui::KeyEvent& e) { if (!m_keyStates[e.keyCode]) { m_keyStates[e.keyCode] = true; auto it = m_keyCodeMap.find((VirtualKey)e.keyCode); if (it != m_keyCodeMap.end()) { // Call all events mapped to that virtual key for (auto& id : it->second) { m_inputs[id].downEvent(e.keyCode); } } } } void InputMapper::onKeyUp(Sender, const vui::KeyEvent& e) { m_keyStates[e.keyCode] = false; auto it = m_keyCodeMap.find((VirtualKey)e.keyCode); if (it != m_keyCodeMap.end()) { // Call all events mapped to that virtual key for (auto& id : it->second) { m_inputs[id].upEvent(e.keyCode); } } } ================================================ FILE: SoA/InputMapper.h ================================================ /// /// InputMapper.h /// Seed of Andromeda /// /// Created by Frank McCoy /// Refactored by Ben Arnold on Mar 25 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles mapping of input for keys and buttons. /// #pragma once #ifndef Input_Manager_h #define Input_Manager_h #include #include #define INPUTMAPPER_DEFAULT_CONFIG_LOCATION "Data/KeyConfig.yml" /// Handles all the user input through the mouse, keyboard and gamepad. /// @author Frank McCoy and Ben Arnold class InputMapper { public: typedef Event::Subscriber Listener; typedef i32 InputID; /// Constructor. InputMapper(); /// Destructor. ~InputMapper(); /// The data for a single Input. class Input { public: Input(InputID ID, const nString& nm, VirtualKey defKey, InputMapper* parent) : id(ID), name(nm), defaultKey(defKey), key(defKey), upEvent(parent), downEvent(parent){ // Empty } InputID id; nString name; ///< The name of the input. VirtualKey defaultKey; ///< The default key. VirtualKey key; ///< The actual key. Event upEvent; ///< The event for when the key is released Event downEvent; ///< The event for when the key is pressed }; typedef std::vector InputList; typedef std::unordered_map InputMap; /// Returns the state of an input /// @param id: The id of the input which is being looked up. /// @return The state of the positive key in the input. bool getInputState(const InputID id); /// Creates a single key input. /// If the input already exists the old ID is returned and no data is modified. /// @param inputName: The name of the input to create. /// @param defaultKey: The default key(positive) for the new input. /// @return The id of the new input. InputID createInput(const nString& inputName, VirtualKey defaultKey); // Single key /// Get the positive key of the supplied input. /// If the input does not exist return UINT32_MAX. /// @param id: The id of the input to look up. /// @return The id of the positive key of the input. VirtualKey getKey(const InputID id); /// Set the positive key of the supplied input. /// @param id: The id of the input to look up. /// @param key: The key to set the keys' positive key to. void setKey(const InputID id, VirtualKey key); /// Resets the axes' positive key to the default. /// @param id: The input to reset to default. void setKeyToDefault(const InputID id); /// Gets the input ID for the supplied input. /// If supplied an invalid inputName the function returns -1. /// @param inputName: The name of the input to look up. /// @return The id of the supplied input. InputID getInputID(const nString& inputName) const; /// Reads all the axes stored in a given ini file. /// @param filePath: The local path to the file to load axes from. void loadInputs(const nString& filePath = INPUTMAPPER_DEFAULT_CONFIG_LOCATION); /// Saves currently stored axes to the given file path. /// @param filePath: The local filePath to the file to save the loaded axes into. void saveInputs(const nString& filePath = INPUTMAPPER_DEFAULT_CONFIG_LOCATION); /// Begins receiving input events from dispatcher void startInput(); /// Stops receiving input events from dispatcher void stopInput(); const bool& isRecievingInput() const { return m_receivingInput; } /// Gets the input associated with the InputID Input& get(InputID i) { return m_inputs[i]; } Input& operator[](InputID i) { return m_inputs[i]; } const InputMap& getInputLookup() const { return m_inputLookup; } private: void onMouseButtonDown(Sender, const vui::MouseButtonEvent& e); void onMouseButtonUp(Sender, const vui::MouseButtonEvent& e); void onKeyDown(Sender, const vui::KeyEvent& e); void onKeyUp(Sender, const vui::KeyEvent& e); InputList m_inputs; ///< All the stored axes. InputMap m_inputLookup; ///< A map of input names to input IDs for quick look up. std::unordered_map > m_keyCodeMap; ///< Map of keycodes to active input /// Assuming vui::MouseButton wont change... bool m_keyStates[VKEY_HIGHEST_VALUE + (ui32)vui::MouseButton::X2]; ///< The state of the keys and mouse buttons this frame. bool m_receivingInput = false; ///< Tracks input reception state AutoDelegatePool m_inputHooks; ///< Stores input reception function hooks for deallocation }; #endif //Input_Manager_h ================================================ FILE: SoA/Inputs.cpp ================================================ #include "stdafx.h" #include "Inputs.h" #include #include "GameManager.h" // Input Commands Sorted Alphabetically InputMapper::InputID INPUT_BACKWARD = -1; InputMapper::InputID INPUT_BLOCK_DRAG = -1; InputMapper::InputID INPUT_BLOCK_SCANNER = -1; InputMapper::InputID INPUT_CROUCH = -1; InputMapper::InputID INPUT_CYCLE_COLOR_FILTER = -1; InputMapper::InputID INPUT_DEBUG = -1; InputMapper::InputID INPUT_DEV_CONSOLE = -1; InputMapper::InputID INPUT_DRAW_MODE = -1; InputMapper::InputID INPUT_EXIT = -1; InputMapper::InputID INPUT_FLASH_LIGHT = -1; InputMapper::InputID INPUT_FLY = -1; InputMapper::InputID INPUT_FORWARD = -1; InputMapper::InputID INPUT_GRID = -1; InputMapper::InputID INPUT_HUD = -1; InputMapper::InputID INPUT_INVENTORY = -1; InputMapper::InputID INPUT_JUMP = -1; InputMapper::InputID INPUT_LEFT = -1; InputMapper::InputID INPUT_LEFT_ROLL = -1; InputMapper::InputID INPUT_MARKER = -1; InputMapper::InputID INPUT_MEGA_SPEED = -1; InputMapper::InputID INPUT_MOUSE_LEFT = -1; InputMapper::InputID INPUT_MOUSE_RIGHT = -1; InputMapper::InputID INPUT_NIGHT_VISION = -1; InputMapper::InputID INPUT_NIGHT_VISION_RELOAD = -1; InputMapper::InputID INPUT_PAUSE = -1; InputMapper::InputID INPUT_PHYSICS_BLOCK_UPDATES = -1; InputMapper::InputID INPUT_PLANET_DRAW_MODE = -1; InputMapper::InputID INPUT_PLANET_ROTATION = -1; InputMapper::InputID INPUT_RANDOM_DEBUG = -1; InputMapper::InputID INPUT_RELOAD_BLOCKS = -1; InputMapper::InputID INPUT_RELOAD_SHADERS = -1; InputMapper::InputID INPUT_RELOAD_SYSTEM = -1; InputMapper::InputID INPUT_RELOAD_TARGET = -1; InputMapper::InputID INPUT_RELOAD_TEXTURES = -1; InputMapper::InputID INPUT_RELOAD_UI = -1; InputMapper::InputID INPUT_RIGHT = -1; InputMapper::InputID INPUT_RIGHT_ROLL = -1; InputMapper::InputID INPUT_SCAN_WSO = -1; InputMapper::InputID INPUT_SCREENSHOT = -1; InputMapper::InputID INPUT_SONAR = -1; InputMapper::InputID INPUT_SPEED_TIME = -1; InputMapper::InputID INPUT_SPRINT = -1; InputMapper::InputID INPUT_TIME_BACK = -1; InputMapper::InputID INPUT_TIME_FORWARD = -1; InputMapper::InputID INPUT_TOGGLE_AR = -1; InputMapper::InputID INPUT_TOGGLE_UI = -1; InputMapper::InputID INPUT_UPDATE_FRUSTUM = -1; InputMapper::InputID INPUT_WATER_UPDATE = -1; InputMapper::InputID INPUT_ZOOM = -1; // Reduce Some Code #define CREATE_INPUT(ID,KEY,VAR) \ VAR = inputManager->createInput(#ID, KEY); // Generate Input Handles void initInputs(InputMapper* inputManager) { // CREATE_INPUT(Random Debug, VKEY_6, INPUT_RANDOM_DEBUG); // The Greatest Input In The Cosmos CREATE_INPUT(Pause, VKEY_ESCAPE, INPUT_PAUSE); // Dev Console CREATE_INPUT(Dev Console, VKEY_GRAVE, INPUT_DEV_CONSOLE); // Game Information CREATE_INPUT(Debug, VKEY_H, INPUT_DEBUG); CREATE_INPUT(Inventory, VKEY_TAB, INPUT_INVENTORY); CREATE_INPUT(HUD, VKEY_T, INPUT_HUD); // Visual Aid CREATE_INPUT(Zoom, VKEY_RCTRL, INPUT_ZOOM); CREATE_INPUT(Sonar, VKEY_R, INPUT_SONAR); CREATE_INPUT(Flash Light, VKEY_L, INPUT_FLASH_LIGHT); CREATE_INPUT(Night Vision, VKEY_N, INPUT_NIGHT_VISION); // Refreshing Functions CREATE_INPUT(Reload Textures, VKEY_F4, INPUT_RELOAD_TEXTURES); CREATE_INPUT(Reload Blocks, VKEY_F6, INPUT_RELOAD_BLOCKS); CREATE_INPUT(Reload Shaders, VKEY_F11, INPUT_RELOAD_SHADERS); CREATE_INPUT(Reload System, VKEY_F10, INPUT_RELOAD_SYSTEM); CREATE_INPUT(Reload UI, VKEY_F5, INPUT_RELOAD_UI); CREATE_INPUT(Reload Night Vision, VKEY_F3, INPUT_NIGHT_VISION_RELOAD); CREATE_INPUT(Reload Target, VKEY_F12, INPUT_RELOAD_TARGET); // Visual Debugging CREATE_INPUT(Grid Toggle, VKEY_G, INPUT_GRID); CREATE_INPUT(Cycle Draw Mode, VKEY_M, INPUT_DRAW_MODE); CREATE_INPUT(Planet Draw Mode, VKEY_J, INPUT_PLANET_DRAW_MODE); CREATE_INPUT(Update Frustum, VKEY_U, INPUT_UPDATE_FRUSTUM); CREATE_INPUT(Cycle Color Filter, VKEY_C, INPUT_CYCLE_COLOR_FILTER); // Movement CREATE_INPUT(Fly, VKEY_F, INPUT_FLY); CREATE_INPUT(Sprint, VKEY_LSHIFT, INPUT_SPRINT); CREATE_INPUT(Crouch, VKEY_LCTRL, INPUT_CROUCH); CREATE_INPUT(Mega Speed, VKEY_LSHIFT, INPUT_MEGA_SPEED); CREATE_INPUT(Jump, VKEY_SPACE, INPUT_JUMP); CREATE_INPUT(Forward, VKEY_W, INPUT_FORWARD); CREATE_INPUT(Left, VKEY_A, INPUT_LEFT); CREATE_INPUT(Right, VKEY_D, INPUT_RIGHT); CREATE_INPUT(Backward, VKEY_S, INPUT_BACKWARD); CREATE_INPUT(Right Roll, VKEY_E, INPUT_RIGHT_ROLL); CREATE_INPUT(Left Roll, VKEY_Q, INPUT_LEFT_ROLL); // Gameplay CREATE_INPUT(Marker, VKEY_C, INPUT_MARKER); CREATE_INPUT(Scan WSO, VKEY_LEFTBRACKET, INPUT_SCAN_WSO); // Physics CREATE_INPUT(Water Update, VKEY_N, INPUT_WATER_UPDATE); CREATE_INPUT(Update Physics Blocks, VKEY_P, INPUT_PHYSICS_BLOCK_UPDATES); // Mouse Buttons CREATE_INPUT(Mouse Right, (VirtualKey)SDL_BUTTON_RIGHT, INPUT_MOUSE_RIGHT); CREATE_INPUT(Mouse Left, (VirtualKey)SDL_BUTTON_LEFT, INPUT_MOUSE_LEFT); // Block Utilities CREATE_INPUT(Block Scanner, VKEY_Q, INPUT_BLOCK_SCANNER); CREATE_INPUT(Block Select, VKEY_B, INPUT_BLOCK_DRAG); // Main Menu CREATE_INPUT(Exit, VKEY_ESCAPE, INPUT_EXIT); CREATE_INPUT(Toggle UI, VKEY_U, INPUT_TOGGLE_UI); CREATE_INPUT(Toggle AR, VKEY_A, INPUT_TOGGLE_AR); CREATE_INPUT(Speed Time, VKEY_LCTRL, INPUT_SPEED_TIME); CREATE_INPUT(Take Screenshot, VKEY_F2, INPUT_SCREENSHOT); CREATE_INPUT(Time Back, VKEY_LEFT, INPUT_TIME_BACK); CREATE_INPUT(Time Forward, VKEY_RIGHT, INPUT_TIME_FORWARD); } ================================================ FILE: SoA/Inputs.h ================================================ /// /// Inputs.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 29 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Hard-coded list of inputs /// TODO: Remove /// #pragma once #ifndef Inputs_h__ #define Inputs_h__ #include "InputMapper.h" // These can not be an enum. They are very likely to change values at runtime! extern InputMapper::InputID INPUT_BACKWARD; extern InputMapper::InputID INPUT_BLOCK_DRAG; extern InputMapper::InputID INPUT_BLOCK_SCANNER; extern InputMapper::InputID INPUT_CROUCH; extern InputMapper::InputID INPUT_CYCLE_COLOR_FILTER; extern InputMapper::InputID INPUT_DEBUG; extern InputMapper::InputID INPUT_DEV_CONSOLE; extern InputMapper::InputID INPUT_DRAW_MODE; extern InputMapper::InputID INPUT_EXIT; extern InputMapper::InputID INPUT_FLASH_LIGHT; extern InputMapper::InputID INPUT_FLY; extern InputMapper::InputID INPUT_FORWARD; extern InputMapper::InputID INPUT_GRID; extern InputMapper::InputID INPUT_HUD; extern InputMapper::InputID INPUT_INVENTORY; extern InputMapper::InputID INPUT_JUMP; extern InputMapper::InputID INPUT_LEFT; extern InputMapper::InputID INPUT_LEFT_ROLL; extern InputMapper::InputID INPUT_MARKER; extern InputMapper::InputID INPUT_MEGA_SPEED; extern InputMapper::InputID INPUT_MOUSE_LEFT; extern InputMapper::InputID INPUT_MOUSE_RIGHT; extern InputMapper::InputID INPUT_NIGHT_VISION; extern InputMapper::InputID INPUT_NIGHT_VISION_RELOAD; extern InputMapper::InputID INPUT_PAUSE; extern InputMapper::InputID INPUT_PHYSICS_BLOCK_UPDATES; extern InputMapper::InputID INPUT_PLANET_DRAW_MODE; extern InputMapper::InputID INPUT_PLANET_ROTATION; extern InputMapper::InputID INPUT_RANDOM_DEBUG; extern InputMapper::InputID INPUT_RELOAD_BLOCKS; extern InputMapper::InputID INPUT_RELOAD_SHADERS; extern InputMapper::InputID INPUT_RELOAD_SYSTEM; extern InputMapper::InputID INPUT_RELOAD_TARGET; extern InputMapper::InputID INPUT_RELOAD_TEXTURES; extern InputMapper::InputID INPUT_RELOAD_UI; extern InputMapper::InputID INPUT_RIGHT; extern InputMapper::InputID INPUT_RIGHT_ROLL; extern InputMapper::InputID INPUT_SCAN_WSO; extern InputMapper::InputID INPUT_SCREENSHOT; extern InputMapper::InputID INPUT_SONAR; extern InputMapper::InputID INPUT_SPEED_TIME; extern InputMapper::InputID INPUT_SPRINT; extern InputMapper::InputID INPUT_TIME_BACK; extern InputMapper::InputID INPUT_TIME_FORWARD; extern InputMapper::InputID INPUT_TOGGLE_AR; extern InputMapper::InputID INPUT_TOGGLE_UI; extern InputMapper::InputID INPUT_UPDATE_FRUSTUM; extern InputMapper::InputID INPUT_WATER_UPDATE; extern InputMapper::InputID INPUT_ZOOM; // Initialize Input IDs At Runtime extern void initInputs(InputMapper* inputManager); #endif // Inputs_h__ ================================================ FILE: SoA/Item.cpp ================================================ #include "stdafx.h" #include "Item.h" ItemPack::ItemPack() : onItemDataAddition(this) { // Add default item append(ItemData()); } ItemID ItemPack::append(ItemData item) { const ItemData* curBlock; ItemID rv; if ((curBlock = hasItem(item.id))) { rv = curBlock->id; item.id = rv; // Overwrite block *const_cast(curBlock) = item; } else { rv = m_itemList.size(); item.id = rv; // Add a new block m_itemList.push_back(item); // Set the correct index m_itemMap[item.name] = rv; } onItemDataAddition(item.id); return rv; } void ItemPack::reserveID(const ItemIdentifier& sid, ItemID id) { if (id >= m_itemList.size()) m_itemList.resize(id + 1); m_itemMap[sid] = id; m_itemList[id].id = id; } ================================================ FILE: SoA/Item.h ================================================ #pragma once #include #include enum class ItemType { NONE, BLOCK, WEAPON, ARMOR, CONSUMABLE, MATERIAL, USABLE, MISC }; typedef nString ItemIdentifier; typedef ui32 ItemID; const nString ITEM_TYPE_STRINGS[] = { "None", "Block", "Weapon", "Armor", "Consumable", "Material", "Usable", "Misc" }; // One or more items. All items are ItemStacks. struct ItemData { ItemIdentifier name = "?"; ItemType type = ItemType::NONE; ItemID id = 0; f32 value = 0.0f; f32 weight = 0.0f; ui32 maxCount = 1; ///< If this is 1, this can only be a single item. // Certain types don't need certain data union { ui32 maxDurability; ui32 blockID; }; }; // Container for all item types // TODO(Ben): Save and load class ItemPack { public: ItemPack(); ItemID append(ItemData item); void reserveID(const ItemIdentifier& sid, ItemID id); const ItemData* hasItem(ItemID id) const { if (id >= m_itemList.size()) { return nullptr; } else { return &m_itemList[id]; } } const ItemData* hasItem(const ItemIdentifier& sid) const { auto v = m_itemMap.find(sid); if (v == m_itemMap.end()) { return nullptr; } else { return &m_itemList[v->second]; } } size_t size() const { return m_itemList.size(); } /************************************************************************/ /* ItemData accessors */ /************************************************************************/ ItemData& operator[](const size_t& index) { return m_itemList[index]; } const ItemData& operator[](const size_t& index) const { return m_itemList[index]; } ItemData& operator[](const ItemIdentifier& sid) { return m_itemList[m_itemMap.at(sid)]; } const ItemData& operator[](const ItemIdentifier& sid) const { return m_itemList[m_itemMap.at(sid)]; } ui16 getItemDataIndex(const ItemIdentifier& sid) const { return m_itemMap.at(sid); } const std::unordered_map& getItemDataMap() const { return m_itemMap; } const std::vector& getItemDataList() const { return m_itemList; } Event onItemDataAddition; ///< Signaled when a block is loaded private: // TODO(Ben): worry about runtime resizing std::unordered_map m_itemMap; ///< Item indices organized by identifiers std::vector m_itemList; ///< Item data list }; struct ItemStack { ItemID id = 0; ui32 count = 0; ui32 durability = 0; ItemPack* pack = nullptr; ///< Flyweight }; ================================================ FILE: SoA/LenseFlareRenderer.cpp ================================================ #include "stdafx.h" #include "LenseFlareRenderer.h" #include "ModPathResolver.h" #include "ShaderLoader.h" #include "Errors.h" #include #include #include #include #include #include struct FlareVertex { f32v2 position; f32v2 uv; f32 offset; }; struct FlareSprite { bool rotate = false; f32 offset; f32 size; ui32 textureIndex; }; KEG_TYPE_DEF_SAME_NAME(FlareSprite, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, FlareSprite, rotate, BOOL); KEG_TYPE_INIT_ADD_MEMBER(kt, FlareSprite, offset, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, FlareSprite, size, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, FlareSprite, textureIndex, UI32); } struct FlareKegProperties { ui32 spritesPerRow = 1; f32 intensity = 1.0f; Array sprites; }; KEG_TYPE_DEF_SAME_NAME(FlareKegProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, FlareKegProperties, spritesPerRow, UI32); KEG_TYPE_INIT_ADD_MEMBER(kt, FlareKegProperties, intensity, F32); kt.addValue("sprites", keg::Value::array(offsetof(FlareKegProperties, sprites), keg::Value::custom(0, "FlareSprite", false))); } const int VERTS_PER_QUAD = 4; const int INDICES_PER_QUAD = 6; LenseFlareRenderer::LenseFlareRenderer() { // Empty } LenseFlareRenderer::~LenseFlareRenderer() { dispose(); } void LenseFlareRenderer::init(const ModPathResolver* textureResolver) { m_textureResolver = textureResolver; } void LenseFlareRenderer::initGL() { { // Load the shader m_program = ShaderLoader::createProgramFromFile("Shaders/LensFlare/flare.vert", "Shaders/LensFlare/flare.frag"); m_unColor = m_program.getUniform("unColor"); // Set constant uniforms m_program.use(); glUniform1i(m_program.getUniform("unTexture"), 2); m_program.unuse(); } { // Load the texture vio::Path path; m_textureResolver->resolvePath("Effects/lens_flares.png", path); vg::ScopedBitmapResource res(vg::ImageIO().load(path)); if (!res.data) { fprintf(stderr, "ERROR: Failed to load Effects/lens_flares.png\n"); } m_texWidth = res.width; m_texHeight = res.height; m_texture = vg::GpuMemory::uploadTexture(&res, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP_MIPMAP); } initMesh(); } void LenseFlareRenderer::render(const f32m4& VP, const f64v3& relCamPos, const f32v3& color, float aspectRatio, f32 size, f32 intensity) { if (size <= 0.0f || intensity <= 0.0f) return; m_program.use(); f32v2 dims(size, size * aspectRatio); // Bind texture glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_texture); // Upload uniforms f32v3 center(-relCamPos); glUniform1f(m_program.getUniform("unIntensity"), intensity * m_intensity); glUniform3fv(m_program.getUniform("unCenter"), 1, ¢er[0]); glUniform3fv(m_program.getUniform("unColor"), 1, &color[0]); glUniform2fv(m_program.getUniform("unDims"), 1, &dims[0]); glUniformMatrix4fv(m_program.getUniform("unVP"), 1, GL_FALSE, &VP[0][0]); glBindVertexArray(m_vao); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDrawElements(GL_TRIANGLES, INDICES_PER_QUAD * m_numSprites, GL_UNSIGNED_SHORT, 0); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); glBindVertexArray(0); m_program.unuse(); } void LenseFlareRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); if (m_texture) { vg::GpuMemory::freeTexture(m_texture); } if (m_vbo) { glDeleteBuffers(1, &m_vbo); m_vbo = 0; } if (m_ibo) { glDeleteBuffers(1, &m_ibo); m_ibo = 0; } if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; } } void LenseFlareRenderer::loadSprites(FlareKegProperties& kegProps) { nString data; vio::IOManager iom; if (!iom.readFileToString("Data/LensFlares/SoA_defaultFlares.yml", data)) { pError("Couldn't find Data/LensFlares/SoA_defaultFlares.yml"); } keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); keg::Error err = keg::parse((ui8*)&kegProps, node, context, &KEG_GLOBAL_TYPE(FlareKegProperties)); if (err != keg::Error::NONE) { fprintf(stderr, "Failed to parse Data/LensFlares/SoA_defaultFlares.yml"); } } void LenseFlareRenderer::initMesh() { FlareKegProperties properties; loadSprites(properties); Array& sprites = properties.sprites; m_numSprites = sprites.size(); m_intensity = properties.intensity; const f32v2 positions[4] = { f32v2(-1.0f, 1.0f), f32v2(-1.0f, -1.0f), f32v2(1.0f, -1.0f), f32v2(1.0f, 1.0f) }; const f32v2 uvs[4] = { f32v2(0.0f, 1.0f), f32v2(0.0f, 0.0f), f32v2(1.0f, 0.0f), f32v2(1.0f, 1.0f) }; ui32 xSprites = properties.spritesPerRow; ui32 spriteDims = m_texWidth / xSprites; if (spriteDims == 0) pError("Lens flare has sprite dims of 0!"); // ui32 ySprites = m_texHeight / spriteDims; f32v2 uvSprite(spriteDims / (f32)m_texWidth, spriteDims / (f32)m_texHeight); const ui16 quadIndices[INDICES_PER_QUAD] = { 0, 1, 2, 2, 3, 0 }; std::vector vertices(m_numSprites * VERTS_PER_QUAD); std::vector indices(m_numSprites * INDICES_PER_QUAD); int index = 0; for (int i = 0; i < m_numSprites; i++) { FlareSprite& s = sprites[i]; f32v2 uvOffset = f32v2(s.textureIndex % xSprites, s.textureIndex / xSprites) * uvSprite; vertices[index].position = positions[0] * s.size; vertices[index + 1].position = positions[1] * s.size; vertices[index + 2].position = positions[2] * s.size; vertices[index + 3].position = positions[3] * s.size; vertices[index].uv = uvs[0] * uvSprite + uvOffset; vertices[index + 1].uv = uvs[1] * uvSprite + uvOffset; vertices[index + 2].uv = uvs[2] * uvSprite + uvOffset; vertices[index + 3].uv = uvs[3] * uvSprite + uvOffset; vertices[index].offset = s.offset; vertices[index + 1].offset = s.offset; vertices[index + 2].offset = s.offset; vertices[index + 3].offset = s.offset; index += 4; } // Set indices for (int i = 0; i < m_numSprites; i++) { for (int j = 0; j < INDICES_PER_QUAD; j++) { indices[i * INDICES_PER_QUAD + j] = quadIndices[j] + i * VERTS_PER_QUAD; } } if (m_vbo == 0) glGenBuffers(1, &m_vbo); if (m_ibo == 0) glGenBuffers(1, &m_ibo); if (m_vao == 0) glGenVertexArrays(1, &m_vao); // Upload data and make VAO glBindVertexArray(m_vao); vg::GpuMemory::bindBuffer(m_vbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::bindBuffer(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_vbo, vg::BufferTarget::ARRAY_BUFFER, sizeof(FlareVertex) * vertices.size(), vertices.data()); vg::GpuMemory::uploadBufferData(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, sizeof(ui16) * indices.size(), indices.data()); m_program.enableVertexAttribArrays(); glVertexAttribPointer(m_program.getAttribute("vPosition"), 2, GL_FLOAT, GL_FALSE, sizeof(FlareVertex), offsetptr(FlareVertex, position)); glVertexAttribPointer(m_program.getAttribute("vUV"), 2, GL_FLOAT, GL_FALSE, sizeof(FlareVertex), offsetptr(FlareVertex, uv)); glVertexAttribPointer(m_program.getAttribute("vOffset"), 1, GL_FLOAT, GL_FALSE, sizeof(FlareVertex), offsetptr(FlareVertex, offset)); glBindVertexArray(0); } ================================================ FILE: SoA/LenseFlareRenderer.h ================================================ /// /// LenseFlareRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 25 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders lense flare for stars and other bright objects /// #pragma once #ifndef LenseFlareRenderer_h__ #define LenseFlareRenderer_h__ #include #include #include class ModPathResolver; struct FlareKegProperties; class LenseFlareRenderer { public: LenseFlareRenderer(); ~LenseFlareRenderer(); void init(const ModPathResolver* textureResolver); void initGL(); void render(const f32m4& VP, const f64v3& relCamPos, const f32v3& color, float aspectRatio, f32 size, f32 intensity); void dispose(); private: void loadSprites(FlareKegProperties& kegProps); void initMesh(); const ModPathResolver* m_textureResolver = nullptr; vg::GLProgram m_program; VGTexture m_texture = 0; ui32 m_texWidth = 0; ui32 m_texHeight = 0; VGBuffer m_vbo = 0; VGBuffer m_ibo = 0; VGVertexArray m_vao = 0; int m_numSprites = 0; f32 m_intensity = 0.0f; VGUniform m_unColor = 0; // TODO(Ben): UBO? }; #endif // LenseFlareRenderer_h__ ================================================ FILE: SoA/LiquidData.h ================================================ #pragma once class LiquidData { public: // TODO: Dafuq, this is a lonely world. i32 startBlockID; i32 numLevels; }; ================================================ FILE: SoA/LiquidVoxelRenderStage.cpp ================================================ #include "stdafx.h" #include "LiquidVoxelRenderStage.h" #include #include "BlockTexturePack.h" #include "Camera.h" #include "Chunk.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "RenderUtils.h" #include "ShaderLoader.h" #include "SoaOptions.h" void LiquidVoxelRenderStage::hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams) { m_renderer = renderer; m_gameRenderParams = gameRenderParams; } void LiquidVoxelRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { ChunkMeshManager* cmm = m_gameRenderParams->chunkMeshmanager; m_renderer->beginLiquid(m_gameRenderParams->blockTexturePack->getAtlasTexture(), m_gameRenderParams->sunlightDirection, m_gameRenderParams->sunlightColor); if (m_gameRenderParams->isUnderwater) glDisable(GL_CULL_FACE); glDepthMask(GL_FALSE); const std::vector & chunkMeshes = cmm->getChunkMeshes(); { std::lock_guard l(cmm->lckActiveChunkMeshes); if (chunkMeshes.empty()) return; for (unsigned int i = 0; i < chunkMeshes.size(); i++) //they are sorted backwards?? { m_renderer->drawLiquid(chunkMeshes[i], m_gameRenderParams->chunkCamera->getPosition(), m_gameRenderParams->chunkCamera->getViewProjectionMatrix()); } } glDepthMask(GL_TRUE); if (m_gameRenderParams->isUnderwater) glEnable(GL_CULL_FACE); m_renderer->end(); } ================================================ FILE: SoA/LiquidVoxelRenderStage.h ================================================ /// /// LiquidVoxelRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the render stage for /// liquid voxel rendering. /// #pragma once #ifndef LiquidVoxelRenderStage_h__ #define LiquidVoxelRenderStage_h__ #include "IRenderStage.h" #include class MeshManager; class GameRenderParams; class ChunkRenderer; class LiquidVoxelRenderStage : public IRenderStage { public: void hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams); /// Draws the render stage virtual void render(const Camera* camera) override; private: ChunkRenderer* m_renderer; const GameRenderParams* m_gameRenderParams = nullptr; ///< Some shared rendering parameters }; #endif // LiquidVoxelRenderStage_h__ ================================================ FILE: SoA/LoadBar.cpp ================================================ #include "stdafx.h" #include "LoadBar.h" #include LoadBarCommonProperties::LoadBarCommonProperties(const f32v2& offset, const f32v2& size, const f32& moveSpeed, const f32v2& textOffset, const f32& textSize) : offsetDirection(offset), size(size), textOffset(textOffset), textSize(textSize), movementSpeed(moveSpeed) { offsetLength = glm::length(offsetDirection); offsetDirection *= (1.0f / offsetLength); } LoadBar::LoadBar(const LoadBarCommonProperties& commonProps) : _commonProps(commonProps), _startPosition(0), _moveDirection(0), _lerpAmount(0.0f), _colorText(0xff, 0xff, 0xff, 0xff), _colorBackground(0x00, 0x00, 0x00, 0xff) { } LoadBar::~LoadBar() { } void LoadBar::expand() { _moveDirection = 1; } void LoadBar::retract() { _moveDirection = -1; } void LoadBar::setCommonProperties(const LoadBarCommonProperties& commonProps) { _commonProps = commonProps; } void LoadBar::setStartPosition(const f32v2& position) { _startPosition = position; } void LoadBar::setText(const nString& text) { _text = text; } void LoadBar::setColor(const ColorRGBA8& colorText, const ColorRGBA8& colorBackground) { _colorText = colorText; _colorBackground = colorBackground; } void LoadBar::update(f32 dt) { if (_moveDirection == 0) return; if (_moveDirection > 0) { // Expand _lerpAmount += _commonProps.movementSpeed * dt; if (_lerpAmount > _commonProps.offsetLength) { _lerpAmount = _commonProps.offsetLength; _moveDirection = 0; } } else { // Retract _lerpAmount -= _commonProps.movementSpeed * dt; if (_lerpAmount < 0.0f) { _lerpAmount = 0.0f; _moveDirection = 0; } } } void LoadBar::draw(vg::SpriteBatch* sb, vg::SpriteFont* sf, ui32 backTexture, f32 depth) { f32v2 endPos = _startPosition + (_commonProps.offsetDirection * _lerpAmount); sb->draw(backTexture, endPos, _commonProps.size, _colorBackground, depth); endPos += _commonProps.textOffset; sb->drawString(sf, _text.c_str(), endPos, _commonProps.textSize, 1.0f, _colorText, vg::TextAlign::TOP_LEFT, depth - 0.001f); } ================================================ FILE: SoA/LoadBar.h ================================================ #pragma once #include #include DECL_VG(class SpriteBatch; class SpriteFont); class LoadBarCommonProperties { public: LoadBarCommonProperties(const f32v2& offset, const f32v2& size, const f32& moveSpeed, const f32v2& textOffset, const f32& textSize); LoadBarCommonProperties() : LoadBarCommonProperties(f32v2(0), f32v2(0), 0, f32v2(0), 0) {} f32v2 offsetDirection; f32 offsetLength; f32v2 size; f32v2 textOffset; f32 textSize; f32 movementSpeed; }; class LoadBar { public: LoadBar(const LoadBarCommonProperties& commonProps); LoadBar() : LoadBar(LoadBarCommonProperties()) {} ~LoadBar(); void expand(); void retract(); bool getIsRetracting() const { return _moveDirection == -1; } bool getIsExpanding() const { return _moveDirection == 1; } bool getIsStationary() const { return _moveDirection == 0; } void setCommonProperties(const LoadBarCommonProperties& commonProps); void setStartPosition(const f32v2& position); void setText(const nString& text); void setColor(const ColorRGBA8& colorText, const ColorRGBA8& colorBackground); void update(f32 dt); void draw(vg::SpriteBatch* sb, vg::SpriteFont* sf, ui32 backTexture, f32 depth); private: nString _text; LoadBarCommonProperties _commonProps; f32v2 _startPosition; i32 _moveDirection; f32 _lerpAmount; ColorRGBA8 _colorText; ColorRGBA8 _colorBackground; }; ================================================ FILE: SoA/LoadContext.h ================================================ /// /// LoadContext.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 4 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// A common context space where assets can /// be loaded. /// #pragma once #ifndef LoadContext_h__ #define LoadContext_h__ #include "ImageAssetLoader.h" #include "ShaderAssetLoader.h" #include class LoadContext { public: void resetWork() { m_totalWork = 0; m_workFinished = 0; } // Call before begin() to indicate how much work is // anticipated. // numTasks is the number of tasks that will be added // via addTask. void addAnticipatedWork(ui32 work) { m_totalWork += work; } void addWorkCompleted(ui32 work) { m_workFinished += work; } f32 getPercentComplete() const { return (f32)m_workFinished / (f32)m_totalWork; } bool isWorkComplete() const { return m_workFinished >= m_totalWork; } struct { ImageAssetLoader image; ShaderAssetLoader shader; } loaders; nString environment; vcore::RPCManager rpcManager; protected: ui32 m_totalWork = 0; volatile ui32 m_workFinished = 0; }; // For when the number of tasks is known class StaticLoadContext : public LoadContext { public: // Call before begin() to indicate how much work is // anticipated. // numTasks is the number of tasks that will be added // via addTask. void addAnticipatedWork(ui32 work) = delete; void addAnticipatedWork(ui32 work, ui32 numTasks) { #ifdef DEBUG if (m_glRPCs) throw 380; #endif m_totalWork += work; m_numTasks += numTasks; } // If using any GL RPCs, be sure to call on GL thread size_t processRequests(size_t maxRequests) { return rpcManager.processRequests(maxRequests); } // Call this after all anticipated work is added, and before // any tasks are added. void begin() { m_glRPCs = new vcore::GLRPC[m_numTasks]; } // It is important that tasks added have been anticipated with // addAnticipatedWork template vcore::GLRPC& addTask(F f, bool blockUntilFinished) { #ifdef DEBUG if (m_freeTask > m_numTasks) throw 381; #endif vcore::GLRPC& rpc = m_glRPCs[m_freeTask++]; rpc.set(f); rpcManager.invoke(&rpc, blockUntilFinished); return rpc; } f32 getPercentComplete() const { return (f32)m_workFinished / (f32)m_totalWork; } bool isWorkComplete() const { return m_workFinished >= m_totalWork; } void blockUntilFinished() { if (m_freeTask) m_glRPCs[m_freeTask - 1].block(); } // Call this when work is completed to free memory used by GLRPCs. void end() { delete[] m_glRPCs; m_glRPCs = nullptr; m_numTasks = 0; m_freeTask = 0; resetWork(); } protected: vcore::GLRPC* m_glRPCs = nullptr; ui32 m_numTasks = 0; volatile ui32 m_freeTask = 0; }; // For when the number of tasks might change class DynamicLoadContext : public LoadContext { public: void resetWork() { m_totalWork = 0; m_workFinished = 0; } // If using any GL RPCs, be sure to call on GL thread size_t processRequests(size_t maxRequests) { return rpcManager.processRequests(maxRequests); } // It is important that tasks added have been anticipated with // addAnticipatedWork template vcore::GLRPC& addTask(F f, bool blockUntilFinished) { m_glRPCs.emplace_back(); vcore::GLRPC& rpc = m_glRPCs.back(); rpc.set(f); rpcManager.invoke(&rpc, blockUntilFinished); return rpc; } f32 getPercentComplete() const { return (f32)m_workFinished / (f32)m_totalWork; } bool isWorkComplete() const { return m_workFinished >= m_totalWork; } void blockUntilFinished() { m_glRPCs.back().block(); } // Call this when work is completed to free memory used by GLRPCs. void clearTasks() { std::list().swap(m_glRPCs); } protected: std::list m_glRPCs; }; #endif // LoadContext_h__ ================================================ FILE: SoA/LoadMonitor.cpp ================================================ #include "stdafx.h" #include "LoadMonitor.h" LoadMonitor::LoadMonitor() : _lock(), _completionCondition() { // Empty } LoadMonitor::~LoadMonitor() { if (_internalTasks.size() > 0) { for (ILoadTask* t : _internalTasks) { if (t) delete t; } } for (auto& t : _internalThreads) { t.detach(); } } void LoadMonitor::addTask(nString name, ILoadTask* task) { _tasks.emplace(name, task); } bool LoadMonitor::isTaskFinished(nString task) { _lock.lock(); bool state = isFinished(task); _lock.unlock(); return state; } bool LoadMonitor::isFinished(nString task) { auto kvp = _tasks.find(task); if (kvp == _tasks.end()) { fprintf(stderr, "LoadMonitor Warning: dependency %s does not exist\n", task.c_str()); return false; } return kvp->second->isFinished(); } bool LoadMonitor::canStart(nString task) { // Check that the dependency exists auto kvp = _tasks.find(task); if (kvp == _tasks.end()) { fprintf(stderr, "LoadMonitor Warning: task %s does not exist\n", task.c_str()); return false; } // Check all dependencies for (auto& dep : kvp->second->dependencies) { if (!isFinished(dep)) { return false; } } return true; } void LoadMonitor::start() { LoadMonitor* monitor = this; for (auto& kvp : _tasks) { ILoadTask* task = kvp.second; nString name = kvp.first; _internalThreads.emplace_back([=] () { // Wait For Dependencies To Complete std::unique_lock uLock(monitor->_lock); _completionCondition.wait(uLock, [=] { #ifdef DEBUG printf("CHECK: %s\r\n", name.c_str()); #endif return monitor->canStart(name); }); uLock.unlock(); #ifdef DEBUG printf("BEGIN: %s\r\n", name.c_str()); task->doWork(); printf("END: %s\r\n", name.c_str()); #else task->doWork(); #endif // DEBUG // Notify That This Task Is Completed uLock.lock(); _completionCondition.notify_all(); uLock.unlock(); }); } } void LoadMonitor::wait() { // Wait for all threads to complete for (auto& t : _internalThreads) { t.join(); t.detach(); } _internalThreads.clear(); // Free all tasks for (ILoadTask* t : _internalTasks) delete t; _internalTasks.clear(); } void LoadMonitor::setDep(nString name, nString dep) { // Check that the task exists auto kvp = _tasks.find(name); if (kvp == _tasks.end()) { fprintf(stderr, "LoadMonitor Warning: Task %s doesn't exist.\n",name.c_str()); return; } // Check that the dependency exists auto dvp = _tasks.find(dep); if (dvp == _tasks.end()) { fprintf(stderr, "LoadMonitor Warning: Dependency %s doesn't exist.\n", dep.c_str()); return; } // Add the dependency kvp->second->dependencies.insert(dep); } ================================================ FILE: SoA/LoadMonitor.h ================================================ #pragma once #include #include #include #include #include // Interface For A Loading Task class ILoadTask { public: ILoadTask() : _isFinished(false) { // Empty } virtual ~ILoadTask(){} bool isFinished() const { return _isFinished; } virtual void load() = 0; void doWork() { load(); _isFinished = true; } std::unordered_set dependencies; private: // Loading State volatile bool _isFinished; }; // Loading Task Closure Wrapper template class LoadFunctor : public ILoadTask { public: LoadFunctor() { // Empty } LoadFunctor(F& f) : _f(f) { // Empty } void load() { // Call The Closure _f(); } private: F _f; }; // Make Use Of Compiler Type Inference For Anonymous Type Closure Wrapper template inline ILoadTask* makeLoader(F f) { return new LoadFunctor(f); } class LoadMonitor { public: LoadMonitor(); ~LoadMonitor(); // Add A Task To This List (No Copy Or Delete Will Be Performed On Task Pointer) void addTask(nString name, ILoadTask* task); template void addTaskF(nString name, F taskF) { ILoadTask* t = makeLoader(taskF); _internalTasks.push_back(t); addTask(name, t); } // Make A Loading Task Dependant On Another (Blocks Until Dependency Completes) void setDep(nString name, nString dep); // Fires Up Every Task In A Separate Thread void start(); // Blocks On Current Thread Until All Child Threads Have Completed void wait(); // Checks If A Task Is Finished bool isTaskFinished(nString task); private: // Is A Task Finished (False If Task Does Not Exist bool isFinished(nString task); // Check Dependencies Of A Task To See If It Can Begin bool canStart(nString task); // Tasks Mapped By Name std::unordered_map _tasks; // Thread For Each Task std::vector _internalThreads; // Functor Wrapper Tasks That Must Be Deallocated By This Monitor std::vector _internalTasks; // Monitor Lock std::mutex _lock; std::condition_variable _completionCondition; }; ================================================ FILE: SoA/LoadTaskBlockData.h ================================================ #pragma once #include "BlockPack.h" #include "BlockLoader.h" #include "BlockTextureLoader.h" #include "BlockTexturePack.h" #include "Errors.h" #include "GameManager.h" #include "LoadContext.h" #include "LoadMonitor.h" #include // This is hacky and temporary, it does way to much class LoadTaskBlockData : public ILoadTask { public: LoadTaskBlockData(BlockPack* blockPack, BlockTextureLoader* loader, StaticLoadContext* context) : blockPack(blockPack), loader(loader), context(context) { context->addAnticipatedWork(50, 0); } virtual void load() { loader->loadTextureData(); // TODO(Ben): Put in state vio::IOManager iom; iom.setSearchDirectory("Data/Blocks/"); // Load in .yml if (!BlockLoader::loadBlocks(iom, blockPack)) { pError("Failed to load Data/Blocks/BlockData.yml"); exit(123456); } context->addWorkCompleted(40); for (size_t i = 0; i < blockPack->size(); i++) { Block& b = blockPack->operator[](i); if (b.active) { loader->loadBlockTextures(b); } } // Set the none textures so we dont get a crash later Block& b = blockPack->operator[]("none"); for (int i = 0; i < 6; i++) { b.textures[i] = loader->getTexturePack()->getDefaultTexture(); } context->addWorkCompleted(10); // Uncomment to Save in .yml BlockLoader::saveBlocks("Data/Blocks/SavedBlockData.yml", blockPack); //{ // For use in pressure explosions // Block visitedNode = Blocks[0]; // visitedNode.name = "Visited Node"; // Blocks.append(&visitedNode, 1); // VISITED_NODE = Blocks["Visited Node"].ID; //} } BlockPack* blockPack; BlockTextureLoader* loader; StaticLoadContext* context; }; ================================================ FILE: SoA/LoadTaskGameManager.h ================================================ #pragma once #include #include "GameManager.h" #include "InputMapper.h" #include "LoadMonitor.h" // Sample Dependency Task class LoadTaskGameManager : public ILoadTask { virtual void load() { GameManager::initializeSystems(); GameManager::registerTexturesForLoad(); } }; ================================================ FILE: SoA/LoadTaskStarSystem.h ================================================ #pragma once #include "LoadMonitor.h" #include "SpaceSystem.h" #include "SoaEngine.h" // Sample Dependency Task class LoadTaskStarSystem : public ILoadTask { friend class MainMenuLoadScreen; LoadTaskStarSystem(const nString& filePath, SoaState* state) : filePath(filePath), soaState(state) { // Empty } virtual void load() { SoaEngine::loadSpaceSystem(soaState, filePath); } nString filePath; SoaState* soaState; }; ================================================ FILE: SoA/LoadTaskTextures.h ================================================ #pragma once #include "LoadMonitor.h" #include "GameManager.h" #include "SoaOptions.h" #include "PlanetGenData.h" // TODO(Ben): Multiple loader threads class LoadTaskTextures : public ILoadTask { virtual void load() { //load the texture pack // GameManager::texturePackLoader->setColorMaps(&PlanetGenData::colorMaps); // GameManager::texturePackLoader->loadAllTextures("Textures/TexturePacks/" + soaOptions.getStringOption("Texture Pack").value + "/"); } }; ================================================ FILE: SoA/MTRenderState.h ================================================ /// /// MTRenderState.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 22 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Render state that needs to be cloned for multi-threading /// to prevent inaccuracies. /// #pragma once #ifndef MTRenderState_h__ #define MTRenderState_h__ #include "Chunk.h" // for DebugChunkData #include "GameSystemComponents.h" #include #include struct DebugChunkData { f64v3 voxelPosition; ChunkGenLevel genLevel; }; /// Not every bit of render state needs to be in MTRenderState. Only things /// that are sensitive, such as fast moving planets and the camera. struct MTRenderState { f64q spaceCameraOrientation; ///< Orientation in space f64v3 spaceCameraPos; ///< Position in space, relative to parent body bool hasVoxelPos; HeadComponent playerHead; VoxelPositionComponent playerPosition; std::map spaceBodyPositions; ///< Space entity positions std::vector debugChunkData; }; #endif // MTRenderState_h__ ================================================ FILE: SoA/MTRenderStateManager.cpp ================================================ #include "stdafx.h" #include "MTRenderStateManager.h" // Increments i in modulo 3 for triple buffering void incrementMod3(OUT int& i) { i = (i + 1) % 3; } MTRenderState* MTRenderStateManager::getRenderStateForUpdate() { std::lock_guard lock(m_lock); // Get the next free state incrementMod3(m_updating); if (m_updating == m_rendering) { incrementMod3(m_updating); } return &m_renderState[m_updating]; } void MTRenderStateManager::finishUpdating() { std::lock_guard lock(m_lock); // Mark the currently updating buffer as the last updated m_lastUpdated = m_updating; } const MTRenderState* MTRenderStateManager::getRenderStateForRender() { std::lock_guard lock(m_lock); // If we haven't updated again, return the same one if (m_rendering == m_lastUpdated) return &m_renderState[m_rendering]; // Render the last updated state m_rendering = m_lastUpdated; return &m_renderState[m_rendering]; } ================================================ FILE: SoA/MTRenderStateManager.h ================================================ /// /// MTRenderStateManager.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 22 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Manages the updating and access of triple buffered /// render state on the render and update threads /// #pragma once #ifndef MTRenderStateManager_h__ #define MTRenderStateManager_h__ #include "MTRenderState.h" #include class MTRenderStateManager { public: /// Gets the state for updating. Only call once per frame. MTRenderState* getRenderStateForUpdate(); /// Marks state as finished updating. Only call once per frame, /// must call for every call to getRenderStateForUpdate. void finishUpdating(); /// Gets the state for rendering. Only call once per frame. const MTRenderState* getRenderStateForRender(); private: int m_updating = 0; ///< Currently updating state int m_lastUpdated = 0; ///< Most recently updated state int m_rendering = 0; ///< Currently rendering state MTRenderState m_renderState[3]; ///< Triple-buffered state std::mutex m_lock; }; #endif // MTRenderStateManager_h__ ================================================ FILE: SoA/MainMenuLoadScreen.cpp ================================================ #include "stdafx.h" #include "MainMenuLoadScreen.h" #include #include #include #include #include "App.h" #include "BlockPack.h" #include "ChunkMeshManager.h" #include "DebugRenderer.h" #include "GameManager.h" #include "InputMapper.h" #include "Inputs.h" #include "LoadTaskGameManager.h" #include "LoadTaskStarSystem.h" #include "MainMenuScreen.h" #include "MusicPlayer.h" #include "SoaFileSystem.h" #include "SoAState.h" const color4 LOAD_COLOR_TEXT(205, 205, 205, 255); const color4 LOAD_COLOR_BG_LOADING(105, 5, 5, 255); const color4 LOAD_COLOR_BG_FINISHED(25, 105, 5, 255); MainMenuLoadScreen::MainMenuLoadScreen(const App* app, CommonState* state, MainMenuScreen* mainMenuScreen) : IAppScreen(app), m_commonState(state), m_mainMenuScreen(mainMenuScreen) { // Empty } i32 MainMenuLoadScreen::getNextScreen() const { return m_app->scrMainMenu->getIndex(); } i32 MainMenuLoadScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void MainMenuLoadScreen::build() { m_env.init(); #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif m_env.addValue("WindowWidth", static_cast(m_game->getWindow().getWidth())); m_env.addValue("WindowHeight", static_cast(m_game->getWindow().getHeight())); m_env.run(nString("Data/Logos/Vorb/ScreenUpdate.lua")); m_vorbScreenDuration = m_env.template getScriptDelegate("VorbMaxDuration")(); m_fUpdateVorbPosition = m_env.template getScriptDelegate("VorbPositionAtTime"); m_fUpdateVorbColor = m_env.template getScriptDelegate("VorbColorAtTime"); m_fUpdateVorbBackColor = m_env.template getScriptDelegate("VorbBackgroundColor"); m_env.run(nString("Data/Logos/Regrowth/ScreenUpdate.lua")); m_regrowthScreenDuration = m_env.template getScriptDelegate("RegrowthMaxDuration")(); m_fUpdateRegrowthPosition = m_env.template getScriptDelegate("RegrowthPositionAtTime"); m_fUpdateRegrowthColor = m_env.template getScriptDelegate("RegrowthColorAtTime"); m_fUpdateRegrowthBackColor = m_env.template getScriptDelegate("RegrowthBackgroundColor"); m_regrowthScale = m_env.template getScriptDelegate("RegrowthScale")(); #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic pop #endif { // Load all textures const cString vorbTexturePaths[VORB_NUM_TEXTURES] = { "Data/Logos/Vorb/V.png", "Data/Logos/Vorb/O.png", "Data/Logos/Vorb/R.png", "Data/Logos/Vorb/B.png", "Data/Logos/Vorb/CubeLeft.png", "Data/Logos/Vorb/CubeRight.png", "Data/Logos/Vorb/CubeTop.png" }; vg::ImageIO imageIO; for (size_t i = 0; i < VORB_NUM_TEXTURES; i++) { vg::Texture& tex = m_vorbTextures[i]; // Load file vg::ScopedBitmapResource bmp(imageIO.load(vorbTexturePaths[i])); tex.width = bmp.width; tex.height = bmp.height; // Create GPU texture glGenTextures(1, &tex.id); glBindTexture(GL_TEXTURE_2D, tex.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data); glBindTexture(GL_TEXTURE_2D, 0); } const cString regrowthTexturePaths[REGROWTH_NUM_TEXTURES] = { "Data/Logos/Regrowth/Regrowth.png", "Data/Logos/Regrowth/Studios.png" }; for (size_t i = 0; i < REGROWTH_NUM_TEXTURES; i++) { vg::Texture& tex = m_regrowthTextures[i]; // Load file vg::ScopedBitmapResource bmp(imageIO.load(regrowthTexturePaths[i])); tex.width = bmp.width; tex.height = bmp.height; // Create GPU texture glGenTextures(1, &tex.id); glBindTexture(GL_TEXTURE_2D, tex.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp.data); glBindTexture(GL_TEXTURE_2D, 0); } } } void MainMenuLoadScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void MainMenuLoadScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { SoaFileSystem fs; fs.init(); MusicPlayer mp; mp.refreshLists(fs); SoaEngine::initState(m_commonState->state); // Make LoadBar Resources m_sb = new vg::SpriteBatch(true, true); m_sf = new vg::SpriteFont(); m_sf->init("Fonts/orbitron_bold-webfont.ttf", 32); // Add Tasks Here addLoadTask("GameManager", "Core Systems", new LoadTaskGameManager); addLoadTask("SpaceSystem", "SpaceSystem", new LoadTaskStarSystem("StarSystems/Trinity", m_commonState->state)); m_monitor.setDep("SpaceSystem", "GameManager"); m_mainMenuScreen->m_renderer.init(m_commonState->window, m_commonState->loadContext, m_mainMenuScreen, m_commonState); m_mainMenuScreen->m_renderer.hook(); m_commonState->loadContext.begin(); m_mainMenuScreen->m_renderer.load(m_commonState->loadContext); // Start the tasks m_monitor.start(); m_isSkipDetected = false; m_timer = 0.0; vui::InputDispatcher::key.onKeyDown += makeDelegate(this, &MainMenuLoadScreen::onKeyPress); } void MainMenuLoadScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_sf->dispose(); delete m_sf; m_sf = nullptr; m_sb->dispose(); delete m_sb; m_sb = nullptr; for (ui32 i = 0; i < m_loadTasks.size(); i++) { // Free memory delete m_loadTasks[i]; m_loadTasks[i] = nullptr; } std::vector().swap(m_loadTasks); // Restore default rasterizer state vg::RasterizerState::CULL_CLOCKWISE.set(); // Free textures for (size_t i = 0; i < VORB_NUM_TEXTURES; i++) glDeleteTextures(1, &m_vorbTextures[i].id); for (size_t i = 0; i < REGROWTH_NUM_TEXTURES; i++) glDeleteTextures(1, &m_regrowthTextures[i].id); vui::InputDispatcher::key.onKeyDown -= makeDelegate(this, &MainMenuLoadScreen::onKeyPress); } void MainMenuLoadScreen::update(const vui::GameTime& gameTime) { // Increment elapsed time m_timer += gameTime.elapsed; if (m_isOnVorb && m_timer > m_vorbScreenDuration) { m_timer = 0.0f; m_isOnVorb = false; } // Perform OpenGL calls m_commonState->loadContext.processRequests(1); // End condition if (m_mainMenuScreen->m_renderer.isLoaded() && m_monitor.isTaskFinished("SpaceSystem") && (m_isSkipDetected || (!m_isOnVorb && m_timer > m_regrowthScreenDuration))) { m_commonState->loadContext.end(); m_state = vui::ScreenState::CHANGE_NEXT; } } void MainMenuLoadScreen::draw(const vui::GameTime&) { static const cString vorbTextureNames[VORB_NUM_TEXTURES] = { "V", "O", "R", "B", "CubeLeft", "CubeRight", "CubeTop" }; static const cString regrowthTextureNames[REGROWTH_NUM_TEXTURES] = { "Regrowth", "Studios" }; const vui::GameWindow& w = m_game->getWindow(); // Clear State For The Screen color4 clearColor; if (m_isOnVorb) { clearColor = m_fUpdateVorbBackColor(m_timer); } else { clearColor = m_fUpdateRegrowthBackColor(m_timer); } glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw vorb logo // Update the window size f32v2 windowSize(w.getWidth(), w.getHeight()); m_env.addValue("WindowWidth", windowSize.x); m_env.addValue("WindowHeight", windowSize.y); // Render each texture m_sb->begin(); f32v2 uvTile(0.999999f, 0.999999f); // TODO: Fix this. if (m_isOnVorb) { for (size_t i = 0; i < VORB_NUM_TEXTURES; i++) { f32v2 pos = m_fUpdateVorbPosition(m_timer, nString(vorbTextureNames[i])); color4 color = m_fUpdateVorbColor(m_timer, nString(vorbTextureNames[i])); m_sb->draw(m_vorbTextures[i].id, nullptr, &uvTile, pos, f32v2(m_vorbTextures[i].width, m_vorbTextures[i].height), color); } } else { if (m_timer <= m_regrowthScreenDuration) { for (size_t i = 0; i < REGROWTH_NUM_TEXTURES; i++) { f32v2 pos = m_fUpdateRegrowthPosition(m_timer, nString(regrowthTextureNames[i])); color4 color = m_fUpdateRegrowthColor(m_timer, nString(regrowthTextureNames[i])); m_sb->draw(m_regrowthTextures[i].id, nullptr, &uvTile, pos, f32v2(m_regrowthTextures[i].width, m_regrowthTextures[i].height) * m_regrowthScale, color); } } else { // Animated loading message. TODO(Ben): Replace with progress bar int it = (int)m_timer % 3; if (it == 0) { m_sb->drawString(m_sf, "Loading.", f32v2(w.getWidth() - m_sf->measure("Loading").x * 0.5, w.getHeight()) / 2.0f, f32v2(1.0), color::White, vg::TextAlign::LEFT); } else if (it == 1) { m_sb->drawString(m_sf, "Loading..", f32v2(w.getWidth() - m_sf->measure("Loading").x * 0.5, w.getHeight()) / 2.0f, f32v2(1.0), color::White, vg::TextAlign::LEFT); } else { m_sb->drawString(m_sf, "Loading...", f32v2(w.getWidth() - m_sf->measure("Loading").x * 0.5, w.getHeight()) / 2.0f, f32v2(1.0), color::White, vg::TextAlign::LEFT); } } } m_sb->end(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_sb->render(windowSize, &vg::SamplerState::LINEAR_CLAMP); // Draw progress f32 progress = m_commonState->loadContext.getPercentComplete(); static f32 maxw = 48.0f; static f32 alpha = 1.0f; if (alpha > 0.0f) { const f32 border = 2.0f; const f32v2 pos(w.getWidth() - maxw * 2.0f, w.getHeight() - maxw * 2.0f); if (progress >= 1.0f) { alpha -= 0.02f; if (alpha < 0.0f) alpha = 0.0f; } f32 width = alpha * maxw; m_sb->begin(); f32v2 size = f32v2(width - border * 2.0f); m_sb->draw(0, pos - size / 2.0f, size, color::Transparent, 0.1f); size = f32v2(width); m_sb->draw(0, pos - size / 2.0f, size, color4(169u, 169u, 169u, (ui8)(alpha * 255.0f)), 0.1f); size = f32v2(progress * width); m_sb->draw(0, pos - size / 2.0f, size, color4(16u, 190u, 239u, ui8(alpha * 255.0f))); m_sb->end(vg::SpriteSortMode::NONE); m_sb->render(f32v2(w.getWidth(), w.getHeight()), nullptr, &vg::DepthState::FULL); } checkGlError("LoadScreen::draw()"); } void MainMenuLoadScreen::addLoadTask(const nString& name, const cString loadText VORB_MAYBE_UNUSED, ILoadTask* task) { // Add the load task to the monitor m_loadTasks.push_back(task); m_monitor.addTask(name, m_loadTasks.back()); } void MainMenuLoadScreen::onKeyPress(Sender, const vui::KeyEvent& e) { switch (e.keyCode) { case VKEY_RETURN: case VKEY_ESCAPE: case VKEY_SPACE: m_isSkipDetected = true; break; default: break; } } ================================================ FILE: SoA/MainMenuLoadScreen.h ================================================ #pragma once #include #include #include #include #include #include #include #include "LoadMonitor.h" #include "LoadBar.h" class App; class MainMenuScreen; struct CommonState; DECL_VG(class SpriteBatch; class SpriteFont); #define VORB_NUM_TEXTURES 7 #define REGROWTH_NUM_TEXTURES 2 class MainMenuLoadScreen : public vui::IAppScreen { public: MainMenuLoadScreen(const App* app, CommonState* state, MainMenuScreen* mainMenuScreen); virtual i32 getNextScreen() const; virtual i32 getPreviousScreen() const; virtual void build(); virtual void destroy(const vui::GameTime& gameTime); virtual void onEntry(const vui::GameTime& gameTime); virtual void onExit(const vui::GameTime& gameTime); virtual void update(const vui::GameTime& gameTime); virtual void draw(const vui::GameTime& gameTime); private: void onKeyPress(Sender, const vui::KeyEvent& e); void addLoadTask(const nString& name, const cString loadText, ILoadTask* task); // Game state CommonState* m_commonState = nullptr; MainMenuScreen* m_mainMenuScreen = nullptr; // Visualization Of Loading Tasks vg::SpriteBatch* m_sb = nullptr; vg::SpriteFont* m_sf = nullptr; // Loading Tasks LoadMonitor m_monitor; std::vector m_loadTasks; // Vars for logos f64 m_timer; f64 m_vorbScreenDuration; f64 m_regrowthScreenDuration; f32 m_regrowthScale; bool m_isSkipDetected; vg::Texture m_vorbTextures[VORB_NUM_TEXTURES]; ///< 7 textures for the letters and cube vg::Texture m_regrowthTextures[REGROWTH_NUM_TEXTURES]; ///< 2 Textures for Regrowth Studios vscript::lua::Environment m_env; Delegate m_fUpdateVorbPosition; ///< f32v2 (f64 totalTime, nString texture) Delegate m_fUpdateVorbColor; ///< f32v4 (f64 totalTime, nString texture) Delegate m_fUpdateVorbBackColor;///< f32v4 (f64 totalTime) Delegate m_fUpdateRegrowthPosition; ///< f32v2 (f64 totalTime, nString texture) Delegate m_fUpdateRegrowthColor; ///< f32v4 (f64 totalTime, nString texture) Delegate m_fUpdateRegrowthBackColor;///< f32v4 (f64 totalTime) bool m_isOnVorb = true; }; ================================================ FILE: SoA/MainMenuRenderer.cpp ================================================ #include "stdafx.h" #include "MainMenuRenderer.h" #include #include #include #include #include #include #include "CommonState.h" #include "Errors.h" #include "GameManager.h" #include "HdrRenderStage.h" #include "MainMenuScreen.h" #include "MainMenuScriptedUI.h" #include "SoaOptions.h" #include "SoAState.h" #include "soaUtils.h" #include "BloomRenderStage.h" void MainMenuRenderer::init(vui::GameWindow* window, StaticLoadContext& context, MainMenuScreen* mainMenuScreen, CommonState* commonState) { m_window = window; m_mainMenuScreen = mainMenuScreen; m_commonState = commonState; m_state = m_commonState->state; vui::InputDispatcher::window.onResize += makeDelegate(this, &MainMenuRenderer::onWindowResize); // TODO(Ben): Dis is bad mkay m_viewport = f32v4(0, 0, m_window->getWidth(), m_window->getHeight()); m_mainMenuUI = &m_mainMenuScreen->m_ui; // Add anticipated work context.addAnticipatedWork(3, 3); // Init render stages m_commonState->stages.skybox.init(window, context); m_commonState->stages.spaceSystem.init(window, context); m_commonState->stages.hdr.init(window, context); stages.colorFilter.init(window, context); stages.exposureCalc.init(window, context); stages.bloom.init(window, context); stages.bloom.setParams(); stages.bloom.setActive(true); // TODO(Cristian): This needs to be upgraded. // Generate terrain patch indices TerrainPatchMesher::generateIndices(); } void MainMenuRenderer::dispose(StaticLoadContext& context) { vui::InputDispatcher::window.onResize -= makeDelegate(this, &MainMenuRenderer::onWindowResize); // Kill the builder if (m_loadThread) { delete m_loadThread; m_loadThread = nullptr; } // TODO(Ben): Dispose common stages stages.colorFilter.dispose(context); stages.exposureCalc.dispose(context); stages.bloom.dispose(context); // Dispose of persistent rendering resources m_hdrTarget.dispose(); m_swapChain.dispose(); } void MainMenuRenderer::load(StaticLoadContext& context) { m_isLoaded = false; m_loadThread = new std::thread([&]() { vcore::GLRPC so[4]; // size_t i = 0; // Create the HDR target // Create the HDR target context.addTask([&](Sender, void*) { Array attachments; vg::GBufferAttachment att[2]; // TODO(Ben): Don't think this is right. // Color att[0].format = vg::TextureInternalFormat::RGBA16F; att[0].pixelFormat = vg::TextureFormat::RGBA; att[0].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[0].number = 1; // Normals att[1].format = vg::TextureInternalFormat::RGBA16F; att[1].pixelFormat = vg::TextureFormat::RGBA; att[1].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[1].number = 2; m_hdrTarget.setSize(m_window->getWidth(), m_window->getHeight()); m_hdrTarget.init(Array(att, 2), vg::TextureInternalFormat::RGBA32F).initDepth(); if (soaOptions.get(OPT_MSAA).value.i > 0) { glEnable(GL_MULTISAMPLE); } else { glDisable(GL_MULTISAMPLE); } context.addWorkCompleted(1); }, false); // Create the swap chain for post process effects (HDR-capable) context.addTask([&](Sender, void*) { m_swapChain.init(m_window->getWidth(), m_window->getHeight(), vg::TextureInternalFormat::RGBA32F); context.addWorkCompleted(1); }, false); // Create full-screen quad context.addTask([&](Sender, void*) { m_commonState->quad.init(); context.addWorkCompleted(1); }, false); // Load all the stages m_commonState->stages.skybox.load(context); m_commonState->stages.spaceSystem.load(context); m_commonState->stages.hdr.load(context); stages.colorFilter.load(context); stages.exposureCalc.load(context); stages.bloom.load(context); context.blockUntilFinished(); m_isLoaded = true; }); m_loadThread->detach(); } void MainMenuRenderer::hook() { m_commonState->stages.skybox.hook(m_state); m_commonState->stages.spaceSystem.hook(m_state, &m_state->clientState.spaceCamera, nullptr); m_commonState->stages.hdr.hook(&m_commonState->quad); stages.colorFilter.hook(&m_commonState->quad); stages.exposureCalc.hook(&m_commonState->quad, &m_hdrTarget, &m_viewport, 1024); stages.bloom.hook(&m_commonState->quad); } void MainMenuRenderer::render() { // Check for window resize if (m_shouldResize) resize(); // Bind the FBO m_hdrTarget.useGeometry(); // Clear depth buffer. Don't have to clear color since skybox will overwrite it glClear(GL_DEPTH_BUFFER_BIT); // Main render passes m_commonState->stages.skybox.render(&m_state->clientState.spaceCamera); // Check fore wireframe mode if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); m_commonState->stages.spaceSystem.setShowAR(m_showAR); m_commonState->stages.spaceSystem.render(&m_state->clientState.spaceCamera); // Restore fill if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); f32v3 colorFilter(1.0); // Color filter rendering if (m_colorFilter != 0) { switch (m_colorFilter) { case 1: colorFilter = f32v3(0.66f); stages.colorFilter.setColor(f32v4(0.0, 0.0, 0.0, 0.33f)); break; case 2: colorFilter = f32v3(0.3f); stages.colorFilter.setColor(f32v4(0.0, 0.0, 0.0, 0.66f)); break; case 3: colorFilter = f32v3(0.0f); stages.colorFilter.setColor(f32v4(0.0, 0.0, 0.0, 0.9f)); break; } stages.colorFilter.render(); } stages.exposureCalc.render(); // Move exposure towards target static const f32 EXPOSURE_STEP = 0.005f; stepTowards(soaOptions.get(OPT_HDR_EXPOSURE).value.f, stages.exposureCalc.getExposure(), EXPOSURE_STEP); // Post processing m_swapChain.reset(0, m_hdrTarget.getGeometryID(), m_hdrTarget.getGeometryTexture(0), soaOptions.get(OPT_MSAA).value.i > 0, false); // TODO: More Effects? // last effect should not swap swapChain if (stages.bloom.isActive()) { stages.bloom.render(); } // Render last glBlendFunc(GL_ONE, GL_ONE); m_commonState->stages.spaceSystem.renderStarGlows(colorFilter); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_swapChain.swap(); m_swapChain.bindPreviousTexture(0); // Draw to backbuffer for the last effect m_swapChain.unuse(m_window->getWidth(), m_window->getHeight()); glDrawBuffer(GL_BACK); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // original depth texture m_hdrTarget.bindDepthTexture(1); m_commonState->stages.hdr.render(&m_state->clientState.spaceCamera); if (m_showUI) m_mainMenuUI->draw(); if (m_shouldScreenshot) dumpScreenshot(); // Check for errors, just in case checkGlError("MainMenuRenderPipeline::render()"); } void MainMenuRenderer::onWindowResize(Sender, const vui::WindowResizeEvent& e) { m_newDims = ui32v2(e.w, e.h); m_shouldResize = true; } void MainMenuRenderer::resize() { m_viewport.z = m_newDims.x; m_viewport.w = m_newDims.y; // TODO(Ben): Fix /* m_hdrFrameBuffer->dispose(); delete m_hdrFrameBuffer; m_swapChain->dispose(); delete m_swapChain; initFramebuffer();*/ m_commonState->stages.spaceSystem.setViewport(m_newDims); stages.exposureCalc.setFrameBuffer(&m_hdrTarget); // Pretty sure we don't need to do this. // m_mainMenuUI->setDimensions(f32v2(m_newDims)); m_shouldResize = false; } void MainMenuRenderer::dumpScreenshot() { // Make screenshots directory vio::buildDirectoryTree("Screenshots"); // Take screenshot dumpFramebufferImage("Screenshots/", m_viewport); m_shouldScreenshot = false; } ================================================ FILE: SoA/MainMenuRenderer.h ================================================ /// /// MainMenuRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the rendering pipeline for the main menu screen. /// #pragma once #ifndef MainMenuRenderer_h__ #define MainMenuRenderer_h__ #include #include #include #include #include #include #include "ExposureCalcRenderStage.h" #include "SpaceSystemRenderStage.h" #include "ColorFilterRenderStage.h" #include "SkyboxRenderStage.h" #include "HdrRenderStage.h" #include "BloomRenderStage.h" #include "LoadContext.h" /// Forward declarations class Camera; class MainMenuScreen; class MainMenuScriptedUI; class MainMenuSystemViewer; class SpaceSystem; struct CommonState; struct SoaState; DECL_VUI(struct WindowResizeEvent; class GameWindow); class MainMenuRenderer { public: /// Initializes the pipeline and passes dependencies void init(vui::GameWindow* window, StaticLoadContext& context, MainMenuScreen* mainMenuScreen, CommonState* commonState); void hook(); // Is asynchronous. Check isLoaded void load(StaticLoadContext& context); void dispose(StaticLoadContext& context); /// Renders the pipeline void render(); void onWindowResize(Sender s, const vui::WindowResizeEvent& e); void takeScreenshot() { m_shouldScreenshot = true; } void setShowUI(bool showUI) { m_showUI = showUI; } void toggleUI() { m_showUI = !m_showUI; } void toggleAR() { m_showAR = !m_showAR; } void toggleWireframe() { m_wireframe = !m_wireframe; } void cycleColorFilter() { m_colorFilter++; if (m_colorFilter > 3) m_colorFilter = 0; } const volatile bool& isLoaded() const { return m_isLoaded; } struct { ColorFilterRenderStage colorFilter; ExposureCalcRenderStage exposureCalc; BloomRenderStage bloom; } stages; private: void resize(); void dumpScreenshot(); vui::GameWindow* m_window; CommonState* m_commonState = nullptr; SoaState* m_state = nullptr; MainMenuScreen* m_mainMenuScreen = nullptr; vg::GBuffer m_hdrTarget; ///< Framebuffer needed for the HDR rendering vg::RTSwapChain<2> m_swapChain; ///< Swap chain of framebuffers used for post-processing MainMenuScriptedUI* m_mainMenuUI; ///< The main menu UI std::thread* m_loadThread = nullptr; volatile bool m_isLoaded = false; ui32v4 m_viewport; ///< Viewport to draw to bool m_showUI = true; bool m_showAR = true; bool m_shouldScreenshot = false; bool m_shouldResize = false; bool m_wireframe = false; ui32v2 m_newDims; int m_colorFilter = 0; }; #endif // MainMenuRenderer_h__ ================================================ FILE: SoA/MainMenuScreen.cpp ================================================ #include "stdafx.h" #include "MainMenuScreen.h" #include #include #include "AmbienceLibrary.h" #include "AmbiencePlayer.h" #include "App.h" #include "DebugRenderer.h" #include "Errors.h" #include "Frustum.h" #include "GameManager.h" #include "GamePlayScreen.h" #include "GameplayLoadScreen.h" #include "InputMapper.h" #include "Inputs.h" #include "MainMenuLoadScreen.h" #include "MainMenuScreen.h" #include "MainMenuSystemViewer.h" #include "SoaOptions.h" #include "SoAState.h" #include "SoaEngine.h" #include "SpaceSystem.h" #include "SpaceSystemUpdater.h" #include "TerrainPatch.h" #include "VoxelEditor.h" MainMenuScreen::MainMenuScreen(const App* app, CommonState* state) : IAppScreen(app), m_commonState(state), m_soaState(state->state), m_updateThread(nullptr), m_threadRunning(false), m_window(state->window) { // Empty } MainMenuScreen::~MainMenuScreen() { // Empty } i32 MainMenuScreen::getNextScreen() const { return m_app->scrGameplayLoad->getIndex(); } i32 MainMenuScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void MainMenuScreen::build() { // Empty } void MainMenuScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void MainMenuScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // Get the state handle m_mainMenuSystemViewer = m_soaState->clientState.systemViewer; m_soaState->clientState.spaceCamera.init(m_window->getAspectRatio()); initInput(); m_soaState->clientState.systemViewer->init(m_window->getViewportDims(), &m_soaState->clientState.spaceCamera, m_soaState->spaceSystem, m_inputMapper); m_mainMenuSystemViewer = m_soaState->clientState.systemViewer; m_ambLibrary = new AmbienceLibrary; m_ambLibrary->addTrack("Menu", "Andromeda Fallen", "Data/Sound/Music/Andromeda Fallen.ogg"); m_ambLibrary->addTrack("Menu", "Brethren", "Data/Sound/Music/Brethren.mp3"); m_ambLibrary->addTrack("Menu", "Crystalite", "Data/Sound/Music/Crystalite.mp3"); m_ambLibrary->addTrack("Menu", "Stranded", "Data/Sound/Music/Stranded.mp3"); m_ambLibrary->addTrack("Menu", "Toxic Haze", "Data/Sound/Music/Toxic Haze.mp3"); m_ambLibrary->addTrack("Menu", "BGM Unknown", "Data/Sound/Music/BGM Unknown.mp3"); m_ambPlayer = new AmbiencePlayer; m_ambPlayer->init(m_commonState->soundEngine, m_ambLibrary); m_spaceSystemUpdater = std::make_unique(); m_spaceSystemUpdater->init(m_soaState); // Initialize the user interface m_formFont.init("Fonts/orbitron_bold-webfont.ttf", 32); initUI(); // Init rendering initRenderPipeline(); // TODO(Ben): Do this or something // Run the update thread for updating the planet //m_updateThread = new std::thread(&MainMenuScreen::updateThreadFunc, this); m_ambPlayer->setVolume(soaOptions.get(OPT_MUSIC_VOLUME).value.f); m_ambPlayer->setToTrack("Menu", 3); m_isFullscreen = soaOptions.get(OPT_BORDERLESS).value.b; m_isBorderless = soaOptions.get(OPT_FULLSCREEN).value.b; } void MainMenuScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { vui::InputDispatcher::window.onResize -= makeDelegate(this, &MainMenuScreen::onWindowResize); vui::InputDispatcher::window.onClose -= makeDelegate(this, &MainMenuScreen::onWindowClose); SoaEngine::optionsController.OptionsChange -= makeDelegate(this, &MainMenuScreen::onOptionsChange); m_renderer.setShowUI(false); m_formFont.dispose(); m_ui.dispose(); m_soaState->clientState.systemViewer->stopInput(); m_threadRunning = false; //m_updateThread->join(); //delete m_updateThread; delete m_inputMapper; m_ambPlayer->dispose(); delete m_ambLibrary; delete m_ambPlayer; } void MainMenuScreen::update(const vui::GameTime& gameTime) { // Check for UI reload if (m_shouldReloadUI) { reloadUI(); } if (m_newGameClicked) newGame("TEST"); if (m_uiEnabled) m_ui.update(gameTime.elapsed); { // Handle time warp const f64 TIME_WARP_SPEED = 1000.0 + (f64)m_inputMapper->getInputState(INPUT_SPEED_TIME) * 10000.0; bool isWarping = false; if (m_inputMapper->getInputState(INPUT_TIME_BACK)) { isWarping = true; m_soaState->time -= TIME_WARP_SPEED; } if (m_inputMapper->getInputState(INPUT_TIME_FORWARD)) { isWarping = true; m_soaState->time += TIME_WARP_SPEED; } if (isWarping) { m_soaState->clientState.spaceCamera.setSpeed(1.0); } else { m_soaState->clientState.spaceCamera.setSpeed(0.3); } } //m_soaState->time += m_soaState->timeStep; m_spaceSystemUpdater->update(m_soaState, m_soaState->clientState.spaceCamera.getPosition(), f64v3(0.0)); m_spaceSystemUpdater->glUpdate(m_soaState); m_mainMenuSystemViewer->update(); m_ambPlayer->update((f32)gameTime.elapsed); m_commonState->soundEngine->update(vsound::Listener()); } void MainMenuScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_soaState->clientState.spaceCamera.updateProjection(); m_renderer.render(); } void MainMenuScreen::initInput() { m_inputMapper = new InputMapper; initInputs(m_inputMapper); // Reload space system event m_inputMapper->get(INPUT_RELOAD_SYSTEM).downEvent += makeDelegate(this, &MainMenuScreen::onReloadSystem); m_inputMapper->get(INPUT_RELOAD_SHADERS).downEvent += makeDelegate(this, &MainMenuScreen::onReloadShaders); m_inputMapper->get(INPUT_RELOAD_UI).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_shouldReloadUI = true; }); m_inputMapper->get(INPUT_TOGGLE_UI).downEvent += makeDelegate(this, &MainMenuScreen::onToggleUI); // TODO(Ben): addFunctor = memory leak m_inputMapper->get(INPUT_TOGGLE_AR).downEvent.addFunctor([&](Sender s VORB_UNUSED, ui32 i VORB_UNUSED) { m_renderer.toggleAR(); }); m_inputMapper->get(INPUT_CYCLE_COLOR_FILTER).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.cycleColorFilter(); }); m_inputMapper->get(INPUT_SCREENSHOT).downEvent.addFunctor([&](Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.takeScreenshot(); }); m_inputMapper->get(INPUT_DRAW_MODE).downEvent += makeDelegate(this, &MainMenuScreen::onToggleWireframe); vui::InputDispatcher::window.onResize += makeDelegate(this, &MainMenuScreen::onWindowResize); vui::InputDispatcher::window.onClose += makeDelegate(this, &MainMenuScreen::onWindowClose); SoaEngine::optionsController.OptionsChange += makeDelegate(this, &MainMenuScreen::onOptionsChange); m_inputMapper->startInput(); } void MainMenuScreen::initRenderPipeline() { //m_renderer.init(m_window, m_commonState->loadContext, this); } void MainMenuScreen::initUI() { m_ui.init(this, &m_app->getWindow(), nullptr, &m_formFont); m_ui.makeViewFromScript("Main Menu", 1, "Data/UI/Forms/main_menu.form.lua"); } void MainMenuScreen::loadGame(const nString& fileName VORB_UNUSED) { //std::cout << "Loading Game: " << fileName << std::endl; //initSaveIomanager(fileName); //// Check the planet string //nString planetName = fileManager.getWorldString(fileName + "/World/"); //if (planetName == "") { // std::cout << "NO PLANET NAME"; // return; //} //m_state = vui::ScreenState::CHANGE_NEXT; } void MainMenuScreen::newGame(const nString& fileName) { if (!m_mainMenuSystemViewer->getSelectedPlanet()) { m_newGameClicked = false; return; } m_soaState->clientState.isNewGame = true; m_soaState->clientState.startSpacePos = m_mainMenuSystemViewer->getClickPos(); f64v3 normal = glm::normalize(m_soaState->clientState.startSpacePos); // Don't spawn underwater if (glm::length(m_soaState->clientState.startSpacePos) < m_mainMenuSystemViewer->getTargetRadius()) { m_soaState->clientState.startSpacePos = normal * m_mainMenuSystemViewer->getTargetRadius(); } // Push out by 5 voxels m_soaState->clientState.startSpacePos += glm::normalize(m_soaState->clientState.startSpacePos) * 5.0 * KM_PER_VOXEL; m_soaState->clientState.startingPlanet = m_mainMenuSystemViewer->getSelectedPlanet(); vecs::EntityID startingPlanet = m_soaState->clientState.startingPlanet; { // Compute start location SpaceSystem* spaceSystem = m_soaState->spaceSystem; auto& arcmp = spaceSystem->axisRotation.getFromEntity(startingPlanet); m_soaState->clientState.startSpacePos = arcmp.currentOrientation * m_soaState->clientState.startSpacePos; } std::cout << "Making new game: " << fileName << std::endl; initSaveIomanager(fileName); m_state = vui::ScreenState::CHANGE_NEXT; } void MainMenuScreen::initSaveIomanager(const vio::Path& savePath) { vio::IOManager& ioManager = m_soaState->saveFileIom; // Make sure the Saves and savePath directories exist ioManager.setSearchDirectory(""); ioManager.makeDirectory("Saves"); ioManager.makeDirectory(savePath); ioManager.setSearchDirectory(savePath); ioManager.makeDirectory("players"); ioManager.makeDirectory("system"); ioManager.makeDirectory("cache"); } void MainMenuScreen::reloadUI() { m_ui.dispose(); initUI(); m_shouldReloadUI = false; printf("UI was reloaded.\n"); } void MainMenuScreen::onReloadSystem(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { SoaEngine::destroySpaceSystem(m_soaState); SoaEngine::loadSpaceSystem(m_soaState, "StarSystems/Trinity"); CinematicCamera tmp = m_soaState->clientState.spaceCamera; // Store camera so the view doesn't change m_soaState->clientState.systemViewer->init(m_window->getViewportDims(), &m_soaState->clientState.spaceCamera, m_soaState->spaceSystem, m_inputMapper); m_soaState->clientState.spaceCamera = tmp; // Restore old camera m_renderer.dispose(m_commonState->loadContext); initRenderPipeline(); } void MainMenuScreen::onReloadShaders(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { printf("Reloading Shaders...\n"); m_renderer.dispose(m_commonState->loadContext); m_renderer.init(m_commonState->window, m_commonState->loadContext, this, m_commonState); m_renderer.hook(); m_commonState->loadContext.begin(); m_renderer.load(m_commonState->loadContext); while (!m_renderer.isLoaded()) { m_commonState->loadContext.processRequests(1); SDL_Delay(1); } m_commonState->loadContext.end(); printf("Done!\n"); } void MainMenuScreen::onQuit(Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { m_window->saveSettings(); SoaEngine::destroyAll(m_soaState); exit(0); } void MainMenuScreen::onWindowResize(Sender s VORB_MAYBE_UNUSED, const vui::WindowResizeEvent& e VORB_MAYBE_UNUSED) { SoaEngine::optionsController.setInt("Screen Width", e.w); SoaEngine::optionsController.setInt("Screen Height", e.h); soaOptions.get(OPT_SCREEN_WIDTH).value.i = e.w; soaOptions.get(OPT_SCREEN_HEIGHT).value.i = e.h; if (m_uiEnabled) m_ui.onOptionsChanged(); m_soaState->clientState.spaceCamera.setAspectRatio(m_window->getAspectRatio()); m_mainMenuSystemViewer->setViewport(ui32v2(e.w, e.h)); } void MainMenuScreen::onWindowClose(Sender s VORB_MAYBE_UNUSED) { onQuit(s, 0); } void MainMenuScreen::onOptionsChange(Sender s VORB_MAYBE_UNUSED) { bool fullscreen = soaOptions.get(OPT_FULLSCREEN).value.b; bool borderless = soaOptions.get(OPT_BORDERLESS).value.b; bool screenChanged = false; ui32v2 screenSize = m_window->getViewportDims(); if (screenSize.x != (ui32)soaOptions.get(OPT_SCREEN_WIDTH).value.i || screenSize.y != (ui32)soaOptions.get(OPT_SCREEN_HEIGHT).value.i) { m_window->setScreenSize(soaOptions.get(OPT_SCREEN_WIDTH).value.i, soaOptions.get(OPT_SCREEN_HEIGHT).value.i); screenChanged = true; } m_window->setFullscreen(fullscreen); m_window->setBorderless(borderless); if (soaOptions.get(OPT_VSYNC).value.b) { m_window->setSwapInterval(vui::GameSwapInterval::V_SYNC); } else { m_window->setSwapInterval(vui::GameSwapInterval::UNLIMITED_FPS); } TerrainPatch::setQuality(soaOptions.get(OPT_PLANET_DETAIL).value.i); m_ambPlayer->setVolume(soaOptions.get(OPT_MUSIC_VOLUME).value.f); // Re-center the window if (screenChanged || m_isFullscreen != fullscreen || m_isBorderless != borderless) { m_isFullscreen = fullscreen; m_isBorderless = borderless; m_window->setPosition(SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } } void MainMenuScreen::onToggleUI(Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.toggleUI(); m_uiEnabled = !m_uiEnabled; if (m_uiEnabled) { initUI(); } else { m_ui.dispose(); } } void MainMenuScreen::onToggleWireframe(Sender s VORB_MAYBE_UNUSED, ui32 i VORB_MAYBE_UNUSED) { m_renderer.toggleWireframe(); } ================================================ FILE: SoA/MainMenuScreen.h ================================================ // // MainMenuScreen.h // Seed Of Andromeda // // Created by Ben Arnold on 17 Oct 2014 // Copyright 2014 Regrowth Studios // MIT License // // This file provides the main menu screen // implementation. This screen encompasses the // gamestate for the main menu, as well as interaction // with the user interface. // #pragma once #ifndef MAINMENUSCREEN_H_ #define MAINMENUSCREEN_H_ #include #include #include #include #include #include "LoadMonitor.h" #include "MainMenuRenderer.h" #include "MainMenuScriptedUI.h" #include "Camera.h" #include "SpaceSystemUpdater.h" class App; class InputMapper; class MainMenuLoadScreen; class MainMenuSystemViewer; struct CommonState; struct SoaState; DECL_VSOUND(class Engine) DECL_VUI(struct WindowResizeEvent); class AmbienceLibrary; class AmbiencePlayer; class MainMenuScreen : public vui::IAppScreen { friend class MainMenuScriptedUI; friend class MainMenuRenderer; friend class MainMenuLoadScreen; // So it can load our assets friend class GameplayLoadScreen; // So it can use our renderer public: MainMenuScreen(const App* app, CommonState* state); ~MainMenuScreen(); virtual i32 getNextScreen() const; virtual i32 getPreviousScreen() const; virtual void build(); virtual void destroy(const vui::GameTime& gameTime); virtual void onEntry(const vui::GameTime& gameTime); virtual void onExit(const vui::GameTime& gameTime); virtual void update(const vui::GameTime& gameTime); virtual void draw(const vui::GameTime& gameTime); // Getters SoaState* getSoAState() const { return m_soaState; } private: /// Initializes event delegates and InputManager void initInput(); /// Initializes the rendering void initRenderPipeline(); /// Initializes user interface void initUI(); /// Loads a save file and prepares to play the game /// @param fileName: The name of the save file void loadGame(const nString& fileName); /// Makes a new save file and prepares to play the game /// @param fileName: The name of the save file void newGame(const nString& fileName); /// Sets up iomanager and makes save file directories if they don't exist void initSaveIomanager(const vio::Path& savePath); /// Reloads the user interface void reloadUI(); /// Cycles the draw mode for wireframe void cycleDrawMode(); // --------------- Event handlers --------------- void onReloadSystem(Sender s, ui32 a); void onReloadShaders(Sender s, ui32 a); void onQuit(Sender s, ui32 a); void onWindowResize(Sender s, const vui::WindowResizeEvent& e); void onWindowClose(Sender s); void onOptionsChange(Sender s); void onToggleUI(Sender s, ui32 i); void onToggleWireframe(Sender s, ui32 i); // ---------------------------------------------- CommonState* m_commonState = nullptr; SoaState* m_soaState = nullptr; vio::IOManager m_ioManager; ///< Helper class for IO operations InputMapper* m_inputMapper = nullptr; std::unique_ptr m_spaceSystemUpdater = nullptr; std::thread* m_updateThread = nullptr; ///< The thread that updates the planet. Runs updateThreadFunc() volatile bool m_threadRunning; ///< True when the thread should be running MainMenuRenderer m_renderer; ///< This handles all rendering for the main menu MainMenuScriptedUI m_ui; ///< The UI form vg::SpriteFont m_formFont; ///< The UI font MainMenuSystemViewer* m_mainMenuSystemViewer = nullptr; AmbienceLibrary* m_ambLibrary = nullptr; AmbiencePlayer* m_ambPlayer = nullptr; vui::GameWindow* m_window = nullptr; bool m_uiEnabled = true; bool m_isFullscreen = false; bool m_isBorderless = false; bool m_newGameClicked = false; bool m_shouldReloadUI = false; }; #endif // MAINMENUSCREEN_H_ ================================================ FILE: SoA/MainMenuScriptedUI.cpp ================================================ #include "stdafx.h" #include "MainMenuScriptedUI.h" #include "SoaEngine.h" #include "SoAState.h" #include "MainMenuScreen.h" #include "MainMenuSystemViewer.h" #include #include #include #include #define ON_TARGET_CHANGE_NAME "onTargetChange" MainMenuScriptedUI::MainMenuScriptedUI() { // Empty } MainMenuScriptedUI::~MainMenuScriptedUI() { // Empty } void MainMenuScriptedUI::init(vui::IGameScreen* ownerScreen, const vui::GameWindow* window, vg::TextureCache* textureCache, vg::SpriteFont* defaultFont /*= nullptr*/, vg::SpriteBatch* spriteBatch /*= nullptr*/) { MainMenuScreen* mainMenuScreen = ((MainMenuScreen*)ownerScreen); m_inputMapper = mainMenuScreen->m_inputMapper; mainMenuScreen->m_mainMenuSystemViewer->TargetChange += makeDelegate(this, &MainMenuScriptedUI::onTargetChange); ScriptedUI::init(ownerScreen, window, textureCache, defaultFont, spriteBatch); } void MainMenuScriptedUI::prepareScriptEnv(ViewEnv* viewEnv) { vui::ScriptedUI::prepareScriptEnv(viewEnv); vscript::lua::Environment* env = static_cast(viewEnv->getEnv()); SoaEngine::optionsController.registerScripting(env); // Controls menu stuff env->setNamespaces("Controls"); env->addCDelegate("size", makeDelegate(this, &MainMenuScriptedUI::getNumInputs)); env->addCDelegate("getInput", makeDelegate(this, &MainMenuScriptedUI::getInput)); env->addCDelegate("getKey", makeDelegate(this, &MainMenuScriptedUI::getKey)); env->addCDelegate("getDefaultKey", makeDelegate(this, &MainMenuScriptedUI::getDefaultKey)); env->addCDelegate("getKeyString", makeDelegate(this, &MainMenuScriptedUI::getKeyString)); env->addCDelegate("getDefaultKeyString", makeDelegate(this, &MainMenuScriptedUI::getDefaultKeyString)); env->addCDelegate("getName", makeDelegate(this, &MainMenuScriptedUI::getName)); env->setNamespaces(); env->addCDelegate("newGame", makeDelegate(this, &MainMenuScriptedUI::newGame)); env->setNamespaces("Game"); env->addCDelegate("exit", makeDelegate(this, &MainMenuScriptedUI::onExit)); // TODO(Ben): Expose and use ECS instead??? env->setNamespaces("Space"); env->addCDelegate("getTargetBody", makeDelegate(this, &MainMenuScriptedUI::getTargetBody)); env->addCDelegate("getBodyName", makeDelegate(this, &MainMenuScriptedUI::getBodyName)); env->addCDelegate("getBodyParentName", makeDelegate(this, &MainMenuScriptedUI::getBodyName)); env->addCDelegate("getBodyTypeName", makeDelegate(this, &MainMenuScriptedUI::getBodyTypeName)); env->addCDelegate("getBodyMass", makeDelegate(this, &MainMenuScriptedUI::getBodyMass)); env->addCDelegate("getBodyDiameter", makeDelegate(this, &MainMenuScriptedUI::getBodyDiameter)); env->addCDelegate("getBodyRotPeriod", makeDelegate(this, &MainMenuScriptedUI::getBodyRotPeriod)); env->addCDelegate("getBodyOrbPeriod", makeDelegate(this, &MainMenuScriptedUI::getBodyOrbPeriod)); env->addCDelegate("getBodyAxialTilt", makeDelegate(this, &MainMenuScriptedUI::getBodyAxialTilt)); env->addCDelegate("getBodyEccentricity", makeDelegate(this, &MainMenuScriptedUI::getBodyEccentricity)); env->addCDelegate("getBodyInclination", makeDelegate(this, &MainMenuScriptedUI::getBodyInclination)); env->addCDelegate("getBodySemiMajor", makeDelegate(this, &MainMenuScriptedUI::getBodySemiMajor)); env->addCDelegate("getGravityAccel", makeDelegate(this, &MainMenuScriptedUI::getGravityAccel)); env->addCDelegate("getVolume", makeDelegate(this, &MainMenuScriptedUI::getVolume)); env->addCDelegate("getAverageDensity", makeDelegate(this, &MainMenuScriptedUI::getAverageDensity)); env->setNamespaces(); } size_t MainMenuScriptedUI::getNumInputs() { return m_inputMapper->getInputLookup().size(); } InputMapper::InputID MainMenuScriptedUI::getInput(int index) { // This is slow, but that is ok. auto it = m_inputMapper->getInputLookup().begin(); std::advance(it, index); return it->second; } VirtualKey MainMenuScriptedUI::getKey(InputMapper::InputID id) { return m_inputMapper->getKey(id); } VirtualKey MainMenuScriptedUI::getDefaultKey(InputMapper::InputID id) { return m_inputMapper->get(id).defaultKey; } nString MainMenuScriptedUI::getKeyString(InputMapper::InputID id) { return nString(VirtualKeyStrings[m_inputMapper->getKey(id)]); } nString MainMenuScriptedUI::getDefaultKeyString(InputMapper::InputID id) { return nString(VirtualKeyStrings[m_inputMapper->get(id).defaultKey]); } nString MainMenuScriptedUI::getName(InputMapper::InputID id) { return m_inputMapper->get(id).name; } void MainMenuScriptedUI::onExit(int code) { ((MainMenuScreen*)m_ownerScreen)->onQuit(this, code); } void MainMenuScriptedUI::onTargetChange(Sender, vecs::EntityID id) { // TODO(Ben): Race condition??? for (auto& view : m_views) { if (view.second.viewport->isEnabled()) { vscript::IEnvironment* env = view.second.viewEnv->getEnv(); Delegate del = env->template getScriptDelegate(ON_TARGET_CHANGE_NAME); if (del != NilDelegate) del(id); } } } void MainMenuScriptedUI::newGame() { ((MainMenuScreen*)m_ownerScreen)->m_newGameClicked = true; } vecs::EntityID MainMenuScriptedUI::getTargetBody() { return ((MainMenuScreen*)m_ownerScreen)->m_mainMenuSystemViewer->getTargetBody(); } nString MainMenuScriptedUI::getBodyName(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return state->spaceSystem->namePosition.getFromEntity(entity).name; } nString MainMenuScriptedUI::getBodyParentName(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; auto parentOID = state->spaceSystem->orbit.getFromEntity(entity).parentOrbId; if (parentOID == 0) return "None"; auto parentNpID = state->spaceSystem->orbit.get(parentOID).npID; return state->spaceSystem->namePosition.get(parentNpID).name; } nString MainMenuScriptedUI::getBodyTypeName(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; auto t = state->spaceSystem->orbit.getFromEntity(entity).type; nString n; switch (t) { case SpaceObjectType::BARYCENTER: n = "Barycenter"; break; case SpaceObjectType::STAR: // TODO(Ben): Spectral classes n = "Star"; break; case SpaceObjectType::PLANET: n = "Planet"; break; case SpaceObjectType::DWARF_PLANET: n = "Dwarf Planet"; break; case SpaceObjectType::MOON: n = "Moon"; break; case SpaceObjectType::DWARF_MOON: n = "Dwarf Moon"; break; case SpaceObjectType::ASTEROID: n = "Asteroid"; break; case SpaceObjectType::COMET: n = "Comet"; break; default: n = "UNKNOWN"; break; } return n; } f32 MainMenuScriptedUI::getBodyMass(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->sphericalGravity.getFromEntity(entity).mass; } f32 MainMenuScriptedUI::getBodyDiameter(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->sphericalGravity.getFromEntity(entity).radius * 2.0f; } f32 MainMenuScriptedUI::getBodyRotPeriod(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->axisRotation.getFromEntity(entity).period; } f32 MainMenuScriptedUI::getBodyOrbPeriod(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->orbit.getFromEntity(entity).t; } f32 MainMenuScriptedUI::getBodyAxialTilt(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->axisRotation.getFromEntity(entity).tilt; } f32 MainMenuScriptedUI::getBodyEccentricity(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->orbit.getFromEntity(entity).e; } f32 MainMenuScriptedUI::getBodyInclination(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->orbit.getFromEntity(entity).i; } f32 MainMenuScriptedUI::getBodySemiMajor(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; return (f32)state->spaceSystem->orbit.getFromEntity(entity).a; } f32 MainMenuScriptedUI::getGravityAccel(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; auto& sgCmp = state->spaceSystem->sphericalGravity.getFromEntity(entity); f32 rad = (f32)(sgCmp.radius * M_PER_KM); return (f32)(M_G * sgCmp.mass / (rad * rad)); } f32 MainMenuScriptedUI::getVolume(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; // TODO(Ben): Handle oblateness auto& sgCmp = state->spaceSystem->sphericalGravity.getFromEntity(entity); f32 rad = (f32)(sgCmp.radius * M_PER_KM); return (f32)(4.0 / 3.0 * M_PI * rad * rad * rad); } f32 MainMenuScriptedUI::getAverageDensity(vecs::EntityID entity) { SoaState* state = ((MainMenuScreen*)m_ownerScreen)->m_soaState; // TODO(Ben): This is a double lookup f32 volume = getVolume(entity); return (f32)(state->spaceSystem->sphericalGravity.getFromEntity(entity).mass / volume); } ================================================ FILE: SoA/MainMenuScriptedUI.h ================================================ /// /// MainMenuScriptedUI.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 13 May 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Custom main menu UI /// #pragma once #ifndef MainMenuScriptedUI_h__ #define MainMenuScriptedUI_h__ #include #include #include #include "InputMapper.h" class MainMenuScreen; class MainMenuScriptedUI : public vui::ScriptedUI { public: MainMenuScriptedUI(); ~MainMenuScriptedUI(); virtual void init(vui::IGameScreen* ownerScreen, const vui::GameWindow* window, vg::TextureCache* textureCache, vg::SpriteFont* defaultFont = nullptr, vg::SpriteBatch* spriteBatch = nullptr) override; protected: virtual void prepareScriptEnv(ViewEnv* env) override; // LUA funcs for controls size_t getNumInputs(); InputMapper::InputID getInput(int index); VirtualKey getKey(InputMapper::InputID id); VirtualKey getDefaultKey(InputMapper::InputID id); nString getKeyString(InputMapper::InputID id); nString getDefaultKeyString(InputMapper::InputID id); nString getName(InputMapper::InputID id); void onExit(int code); void onTargetChange(Sender s, vecs::EntityID id); void newGame(); // Planet functions (temporary???) vecs::EntityID getTargetBody(); nString getBodyName(vecs::EntityID entity); nString getBodyParentName(vecs::EntityID entity); nString getBodyTypeName(vecs::EntityID entity); f32 getBodyMass(vecs::EntityID entity); f32 getBodyDiameter(vecs::EntityID entity); f32 getBodyRotPeriod(vecs::EntityID entity); f32 getBodyOrbPeriod(vecs::EntityID entity); f32 getBodyAxialTilt(vecs::EntityID entity); f32 getBodyEccentricity(vecs::EntityID entity); f32 getBodyInclination(vecs::EntityID entity); f32 getBodySemiMajor(vecs::EntityID entity); f32 getGravityAccel(vecs::EntityID entity); f32 getVolume(vecs::EntityID entity); f32 getAverageDensity(vecs::EntityID entity); InputMapper* m_inputMapper = nullptr; }; #endif // MainMenuScriptedUI_h__ ================================================ FILE: SoA/MainMenuSystemViewer.cpp ================================================ #include "stdafx.h" #include "MainMenuSystemViewer.h" #include #include #include "Camera.h" #include "SpaceSystem.h" #include "TerrainPatch.h" #include "SphericalHeightmapGenerator.h" const f32 MainMenuSystemViewer::MIN_SELECTOR_SIZE = 12.0f; const f32 MainMenuSystemViewer::MAX_SELECTOR_SIZE = 160.0f; void MainMenuSystemViewer::init(ui32v2 viewport, CinematicCamera* camera, SpaceSystem* spaceSystem, InputMapper* inputManager) { m_viewport = viewport; m_camera = camera; m_spaceSystem = spaceSystem; m_inputManager = inputManager; mouseButtons[0] = false; mouseButtons[1] = false; // Initialize the camera m_camera->setPosition(f64v3(0.0, 200000.0, 0.0)); // Initialize the camera m_camera->setClippingPlane(10000.0f, 30000000000000.0f); m_camera->setTarget(f64v3(0.0, 0.0, 0.0), f32v3(1.0f, 0.0f, 0.0f), f32v3(0.0f, 0.0f, 1.0f), 20000.0); targetBody("Aldrin"); // Force target focal point m_camera->setFocalPoint(getTargetPosition() - f64v3(glm::normalize(m_camera->getDirection())) * getTargetRadius()); // Register events startInput(); } void MainMenuSystemViewer::dispose() { stopInput(); } void MainMenuSystemViewer::update() { const f32 HOVER_SPEED = 0.08f; const f32 HOVER_SIZE_INC = 7.0f; m_camera->setClippingPlane((f32)(0.1 * KM_PER_M), m_camera->getFarClip()); // Target closest point on sphere m_camera->setTargetFocalPoint(getTargetPosition() - f64v3(glm::normalize(m_camera->getDirection())) * getTargetRadius()); m_camera->update(); for (auto& it : m_spaceSystem->namePosition) { vecs::ComponentID componentID; f64v3 relativePos = it.second.position - m_camera->getPosition(); f64 distance = glm::length(relativePos); f64 radiusPixels; f64 radius; BodyArData& data = bodyArData[it.first]; f32 hoverTime = data.hoverTime; if (m_camera->pointInFrustum(f32v3(relativePos))) { data.inFrustum = true; // Get screen position f32v3 screenCoords = m_camera->worldToScreenPoint(relativePos); f32v2 xyScreenCoords(screenCoords.x * m_viewport.x, screenCoords.y * m_viewport.y); // Get a smooth interpolator with hermite f32 interpolator = hermite(hoverTime); // See if it has a radius componentID = m_spaceSystem->sphericalGravity.getComponentID(it.first); if (componentID) { // Get radius of projected sphere radius = m_spaceSystem->sphericalGravity.get(componentID).radius; radiusPixels = (radius / (tan((f64)m_camera->getFieldOfView() / 2.0) * distance)) * ((f64)m_viewport.y / 2.0); } else { radius = 1000.0; radiusPixels = (radius / (tan((f64)m_camera->getFieldOfView() / 2.0) * distance)) * ((f64)m_viewport.y / 2.0); } f32 selectorSize = (f32)(radiusPixels * 2.0 + 3.0); if (selectorSize < MIN_SELECTOR_SIZE) selectorSize = MIN_SELECTOR_SIZE; // Interpolate size selectorSize += interpolator * HOVER_SIZE_INC; // Detect mouse hover if (glm::length(m_mouseCoords - xyScreenCoords) <= selectorSize / 2.0f) { data.isHovering = true; data.hoverEntity = it.first; hoverTime += HOVER_SPEED; if (hoverTime > 1.0f) hoverTime = 1.0f; } else { data.isHovering = false; hoverTime -= HOVER_SPEED; if (hoverTime < 0.0f) hoverTime = 0.0f; } data.hoverTime = hoverTime; data.selectorSize = selectorSize; } else { data.isHovering = false; data.inFrustum = false; } } } void MainMenuSystemViewer::startInput() { stopInput(); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [=](Sender s, const vui::MouseButtonEvent& e) { onMouseButtonDown(s, e); }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [=](Sender s, const vui::MouseButtonEvent& e) { onMouseButtonUp(s, e); }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [=](Sender s, const vui::MouseMotionEvent& e) { onMouseMotion(s, e); }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onWheel, [=](Sender s, const vui::MouseWheelEvent& e) { onMouseWheel(s, e); }); } void MainMenuSystemViewer::stopInput() { m_hooks.dispose(); } void MainMenuSystemViewer::targetBody(const nString& name) { for (auto& it : m_spaceSystem->namePosition) { if (it.second.name == name) { targetBody(it.first); return; } } } void MainMenuSystemViewer::targetBody(vecs::EntityID eid) { if (m_targetEntity != eid) { TargetChange(eid); m_targetEntity = eid; m_targetComponent = m_spaceSystem->namePosition.getComponentID(m_targetEntity); } } f64v3 MainMenuSystemViewer::getTargetPosition() { return m_spaceSystem->namePosition.get(m_targetComponent).position; } f64 MainMenuSystemViewer::getTargetRadius() { return m_spaceSystem->sphericalGravity.getFromEntity(m_targetEntity).radius; } nString MainMenuSystemViewer::getTargetName() { return m_spaceSystem->namePosition.get(m_targetComponent).name; } void MainMenuSystemViewer::onMouseButtonDown(Sender sender VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { m_mouseCoords.x = (f32)e.x; m_mouseCoords.y = (f32)e.y; if (e.button == vui::MouseButton::LEFT) { mouseButtons[0] = true; // Target a body if we clicked on one f64 closestDist = 99999999999999999999999999999.0; vecs::EntityID closestEntity = 0; for (auto& it : bodyArData) { if (it.second.isHovering) { // Check distance so we pick only the closest one f64v3 pos = m_spaceSystem->namePosition.getFromEntity(it.first).position; f64 dist = glm::length(pos - m_camera->getPosition()); if (dist < closestDist) { closestDist = dist; closestEntity = it.first; } else { it.second.isLandSelected = false; } } } // If we selected an entity, then do the target picking if (closestEntity) { targetBody(closestEntity); pickStartLocation(closestEntity); } } else { mouseButtons[1] = true; } } void MainMenuSystemViewer::onMouseButtonUp(Sender sender VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { m_mouseCoords.x = (f32)e.x; m_mouseCoords.y = (f32)e.y; if (e.button == vui::MouseButton::LEFT) { mouseButtons[0] = false; } else { mouseButtons[1] = false; } } void MainMenuSystemViewer::onMouseWheel(Sender sender VORB_MAYBE_UNUSED, const vui::MouseWheelEvent& e) { #define SCROLL_SPEED 0.1f m_camera->offsetTargetFocalLength((f32)m_camera->getTargetFocalLength() * SCROLL_SPEED * -e.dy); if (m_camera->getTargetFocalLength() < 0.1f) { m_camera->setTargetFocalLength(0.1f); } } void MainMenuSystemViewer::onMouseMotion(Sender sender VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { m_mouseCoords.x = (f32)e.x; m_mouseCoords.y = (f32)e.y; #define MOUSE_SPEED 0.001f if (mouseButtons[0]) { m_camera->rotateFromMouse((f32)-e.dx, (f32)-e.dy, MOUSE_SPEED); } if (mouseButtons[1]) { m_camera->rollFromMouse((f32)e.dx, MOUSE_SPEED); } } void MainMenuSystemViewer::pickStartLocation(vecs::EntityID eid) { // Check to see if it even has terrain by checking if it has a generator if (!m_spaceSystem->sphericalTerrain.getFromEntity(m_targetEntity).cpuGenerator) return; // Select the planet m_selectedPlanet = eid; f32v2 ndc = f32v2((m_mouseCoords.x / m_viewport.x) * 2.0f - 1.0f, 1.0f - (m_mouseCoords.y / m_viewport.y) * 2.0f); f64v3 pickRay(m_camera->getPickRay(ndc)); vecs::ComponentID cid = m_spaceSystem->namePosition.getComponentID(eid); if (!cid) return; f64v3 centerPos = m_spaceSystem->namePosition.get(cid).position; f64v3 pos = f64v3(centerPos - m_camera->getPosition()); cid = m_spaceSystem->sphericalGravity.getComponentID(eid); if (!cid) return; f64 radius = m_spaceSystem->sphericalGravity.get(cid).radius; // Compute the intersection f64v3 normal, hitpoint; f64 distance; if (IntersectionUtils::sphereIntersect(pickRay, f64v3(0.0f), pos, radius, hitpoint, distance, normal)) { hitpoint -= pos; cid = m_spaceSystem->axisRotation.getComponentID(eid); if (cid) { f64q rot = m_spaceSystem->axisRotation.get(cid).currentOrientation; hitpoint = glm::inverse(rot) * hitpoint; } // Compute face and grid position PlanetHeightData heightData; m_spaceSystem->sphericalTerrain.getFromEntity(m_targetEntity).cpuGenerator->generateHeightData(heightData, glm::normalize(hitpoint)); f64 height = heightData.height * KM_PER_VOXEL; m_clickPos = f64v3(hitpoint + glm::normalize(hitpoint) * height); auto& data = bodyArData[eid]; data.selectedPos = hitpoint; data.isLandSelected = true; } } ================================================ FILE: SoA/MainMenuSystemViewer.h ================================================ /// /// MainMenuSystemViewer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 3 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Controls the camera and state when viewing/selecting /// the star system in the main menu. /// #pragma once #ifndef MainMenuSystemViewer_h__ #define MainMenuSystemViewer_h__ #include // Temporary #include #include #include "VoxelCoordinateSpaces.h" class CinematicCamera; class InputMapper; class SpaceSystem; class MainMenuSystemViewer { public: void init(ui32v2 viewport, CinematicCamera* camera, SpaceSystem* spaceSystem, InputMapper* inputManager); void dispose(); void setViewport(const ui32v2& viewPort) { m_viewport = viewPort; } void update(); void startInput(); void stopInput(); struct BodyArData { f32 hoverTime = 0.0f; f32 selectorSize = 0.0f; bool inFrustum = false; vecs::EntityID hoverEntity = 0; bool isHovering = false; bool isLandSelected = false; f32v3 selectedPos; }; const BodyArData* finBodyAr(vecs::EntityID eid) const { auto it = bodyArData.find(eid); if (it == bodyArData.end()) return nullptr; return &it->second; } /// Targets a named body /// @param name: Name of the body void targetBody(const nString& name); /// Targets an entity /// @param eid: Entity ID void targetBody(vecs::EntityID eid); /// Getters vecs::EntityID getSelectedPlanet() const { return m_selectedPlanet; } vecs::EntityID getTargetBody() const { return m_targetEntity; } const f64v3& getClickPos() const { return m_clickPos; } /// Gets the position of the targeted entity /// @return position f64v3 getTargetPosition(); /// Gets the position of the targeted entity /// @return radius f64 getTargetRadius(); /// Gets the name of the targeted component /// @return position nString getTargetName(); static const f32 MIN_SELECTOR_SIZE; static const f32 MAX_SELECTOR_SIZE; Event TargetChange; private: // Events AutoDelegatePool m_hooks; ///< Input hooks reservoir void onMouseButtonDown(Sender sender, const vui::MouseButtonEvent& e); void onMouseButtonUp(Sender sender, const vui::MouseButtonEvent& e); void onMouseWheel(Sender sender, const vui::MouseWheelEvent& e); void onMouseMotion(Sender sender, const vui::MouseMotionEvent& e); void pickStartLocation(vecs::EntityID eid); nString currentBody = ""; std::map bodyArData; vecs::EntityID m_targetEntity = 1; ///< Current entity we are focusing on vecs::ComponentID m_targetComponent = 1; ///< namePositionComponent of the targetEntity bool mouseButtons[2]; f32v2 m_mouseCoords = f32v2(-1.0f); f32v2 m_viewport; f64v3 m_clickPos = f64v3(0.0); vecs::EntityID m_selectedPlanet = 0; CinematicCamera* m_camera = nullptr; SpaceSystem* m_spaceSystem = nullptr; InputMapper* m_inputManager = nullptr; }; #endif // MainMenuSystemViewer_h__ ================================================ FILE: SoA/MarchingCubesTable.h ================================================ /// /// MarchingCubesTable.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 14 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Tables for marching cubes algorithm /// #pragma once #ifndef MarchingCubesTable_h__ #define MarchingCubesTable_h__ // Tables used by Marching Cubes Algorithm // these tables came from Paul Baurke's web page at // http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ static int edgeTable[256] = { 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 }; static int triTable[256][16] = { { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; #endif // MarchingCubesTable_h__ ================================================ FILE: SoA/MaterialAtlas.h ================================================ #pragma once class TextureAtlas { public: }; ================================================ FILE: SoA/MaterialData.h ================================================ #pragma once class TextureData { public: f32 weight; f32 volume; }; ================================================ FILE: SoA/MaterialStack.h ================================================ #pragma once class TextureStack { public: i32 id; i32 count; }; ================================================ FILE: SoA/MetaSection.cpp ================================================ #include "stdafx.h" #include "MetaSection.h" ================================================ FILE: SoA/MetaSection.h ================================================ // // MetaSection.h // Seed of Andromeda // // Created by Cristian Zaloj on 25 May 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Provides a method of // #pragma once #ifndef MetaSection_h__ #define MetaSection_h__ #include typedef ui32 MetaID; typedef void* MetaData; /*! @brief A description of a field in the metadata */ struct MetaFieldInformation { MetaID id; ///< The unique ID of the field UNIT_SPACE(BYTES) size_t offset; ///< The offset of the value in the section UNIT_SPACE(BYTES) size_t size; ///< The total size of the section nString name; ///< The section's string description }; /*! @brief Stores and keeps track of all metadata fields a metadata section will have. */ class MetaSection { public: template static T* retrieve(const MetaData& data, const MetaFieldInformation& information) { return (T*)((ui8*)data + information.offset); } MetaID add(size_t s, const nString& name, size_t alignment = 1); MetaFieldInformation& operator[] (MetaID id); const MetaFieldInformation& operator[] (MetaID id) const; /*! @brief Retrieve the total size of the metadata section to be able to accommodate all fields. * * @return The total size of the section. */ UNIT_SPACE(BYTES) const size_t& getTotalSize() const { return m_size; } private: std::vector m_fields; ///< All the fields that this metadata section contains size_t m_size = 0; ///< The total size of a meta-section }; #endif // MetaSection_h__ ================================================ FILE: SoA/ModInformation.h ================================================ // // ModInformation.h // Seed of Andromeda // // Created by Cristian Zaloj on 4 Jul 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef ModInformation_h__ #define ModInformation_h__ #include #include "DLLAPI.h" typedef Delegate struct ModInformation { DLLAPI::Func* funcs; size_t funcCount; }; #endif // ModInformation_h__ ================================================ FILE: SoA/ModPathResolver.cpp ================================================ #include "stdafx.h" #include "ModPathResolver.h" void ModPathResolver::init(const vio::Path& defaultPath, const vio::Path& modPath) { setDefaultDir(defaultPath); setModDir(modPath); } void ModPathResolver::setDefaultDir(const vio::Path& path) { defaultIom.setSearchDirectory(path); } void ModPathResolver::setModDir(const vio::Path& path) { modIom.setSearchDirectory(path); } bool ModPathResolver::resolvePath(const vio::Path& path, vio::Path& resultAbsolutePath, bool printModNotFound /* = false */) const { if (!modIom.resolvePath(path, resultAbsolutePath)) { if (printModNotFound) { printf("Did not find path %s in %s.", path.getCString(), modIom.getSearchDirectory().getCString()); } if (!defaultIom.resolvePath(path, resultAbsolutePath)) { if (printModNotFound) { printf("Did not find path %s in %s.", path.getCString(), defaultIom.getSearchDirectory().getCString()); } return false; } } return true; } ================================================ FILE: SoA/ModPathResolver.h ================================================ /// /// ModPathResolver.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 19 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Resolves file paths for mod files by first looking in /// a mod folder, then a default if it fails. /// #pragma once #ifndef ModPathResolver_h__ #define ModPathResolver_h__ #include class ModPathResolver { public: /// Initialization void init(const vio::Path& defaultPath, const vio::Path& modPath); /// Sets directory for defaults void setDefaultDir(const vio::Path& path); /// Sets directory for mods void setModDir(const vio::Path& path); /// Gets the absolute path. If not in Mod, checks in default. /// @return false on failure bool resolvePath(const vio::Path& path, vio::Path& resultAbsolutePath, bool printModNotFound = false) const; vio::IOManager defaultIom; vio::IOManager modIom; }; #endif // ModPathResolver_h__ ================================================ FILE: SoA/ModelMesher.cpp ================================================ #include "stdafx.h" #include "ModelMesher.h" #include "VoxelMatrix.h" #include "VoxelModel.h" #include "VoxelModelMesh.h" #include "MarchingCubesTable.h" #include "DualContouringMesher.h" #include const f32v3 VOXEL_MODEL[24] = { f32v3(0, 1, 0), f32v3(0, 1, 1), f32v3(0, 0, 0), f32v3(0, 0, 1), f32v3(1, 1, 1), f32v3(1, 1, 0), f32v3(1, 0, 1), f32v3(1, 0, 0), f32v3(0, 0, 1), f32v3(1, 0, 1), f32v3(0, 0, 0), f32v3(1, 0, 0), f32v3(0, 1, 0), f32v3(1, 1, 0), f32v3(0, 1, 1), f32v3(1, 1, 1), f32v3(1, 1, 0), f32v3(0, 1, 0), f32v3(1, 0, 0), f32v3(0, 0, 0), f32v3(0, 1, 1), f32v3(1, 1, 1), f32v3(0, 0, 1), f32v3(1, 0, 1) }; const ui32 VOXEL_INDICES[6] = { 0, 2, 1, 1, 2, 3 }; const i32v3 VOXEL_SIDES[6] = { i32v3(-1, 0, 0), i32v3(1, 0, 0), i32v3(0, -1, 0), i32v3(0, 1, 0), i32v3(0, 0, -1), i32v3(0, 0, 1), }; VoxelModelMesh ModelMesher::createMesh(const VoxelModel* model) { std::vector vertices; std::vector indices; VoxelModelMesh rv; genMatrixMesh(model->getMatrix(), vertices, indices); if (indices.size() == 0) return rv; glGenVertexArrays(1, &rv.m_vao); glBindVertexArray(rv.m_vao); glGenBuffers(1, &rv.m_vbo); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VoxelModelVertex), vertices.data(), GL_STATIC_DRAW); rv.m_indCount = indices.size(); rv.m_triCount = (indices.size() * 2) / 6; glGenBuffers(1, &rv.m_ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, rv.m_indCount * sizeof(ui32), indices.data(), GL_STATIC_DRAW); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(rv.m_vao); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBindVertexArray(0); // THIS CAUSES CRASH v v v glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); return rv; } VoxelModelMesh ModelMesher::createMarchingCubesMesh(const VoxelModel* model) { std::vector vertices; std::vector indices; VoxelModelMesh rv; auto& matrix = model->getMatrix(); // This constructs a potential (density) field from our voxel data, with points at the corner of each voxel f32v4* points = new f32v4[(matrix.size.x + 1) * (matrix.size.y + 1) * (matrix.size.z + 1)]; int index = 0; // + 1 since its the corner points intead of the actual voxels (for smoother mesh) for (ui32 x = 0; x < matrix.size.x + 1; x++) { for (ui32 y = 0; y < matrix.size.y + 1; y++) { for (ui32 z = 0; z < matrix.size.z + 1; z++) { f32v4 vert(x, y, z, 0); // Gets the potential vert.w = getMarchingPotential(matrix, x, y, z); points[x * (matrix.size.y + 1) * (matrix.size.z + 1) + y * (matrix.size.z + 1) + z] = vert; index++; } } } marchingCubes(matrix, 1.0f, 1.0f, 1.0f, 0.5f, points, vertices); // TODO(Ben): Indexed drawing rv.m_triCount = vertices.size() / 3; delete[] points; glGenVertexArrays(1, &rv.m_vao); glBindVertexArray(rv.m_vao); glGenBuffers(1, &rv.m_vbo); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VoxelModelVertex), vertices.data(), GL_STATIC_DRAW); rv.m_indCount = 0; glGenBuffers(1, &rv.m_ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, rv.m_indCount * sizeof(ui32), indices.data(), GL_STATIC_DRAW); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(rv.m_vao); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBindVertexArray(0); // THIS CAUSES CRASH v v v glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); return rv; } VoxelModelMesh ModelMesher::createDualContouringMesh(const VoxelModel* model) { std::vector vertices; std::vector indices; VoxelModelMesh rv; DualContouringMesher::genMatrixMesh(model->getMatrix(), vertices, indices); if (indices.size() == 0) return rv; glGenVertexArrays(1, &rv.m_vao); glBindVertexArray(rv.m_vao); glGenBuffers(1, &rv.m_vbo); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VoxelModelVertex), vertices.data(), GL_STATIC_DRAW); rv.m_indCount = indices.size(); rv.m_triCount = (indices.size() * 2) / 6; glGenBuffers(1, &rv.m_ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, rv.m_indCount * sizeof(ui32), indices.data(), GL_STATIC_DRAW); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindVertexArray(rv.m_vao); glBindBuffer(GL_ARRAY_BUFFER, rv.m_vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rv.m_ibo); glBindVertexArray(0); // THIS CAUSES CRASH v v v glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); return rv; } // Gets the potential at voxel corners f32 ModelMesher::getMarchingPotential(const VoxelMatrix& matrix, int x, int y, int z) { // TODO: This could all be written from scratch. Not sure the best way to go about it. f32 potential = 0.0f; const int FILTER_SIZE = 2; ///< Should be at least 2 const int HALF_FILTER_SIZE = FILTER_SIZE / 2; x -= HALF_FILTER_SIZE; y -= HALF_FILTER_SIZE; z -= HALF_FILTER_SIZE; // We use a 2x2x2 filter of voxels around a given voxel corner point for (int i = 0; i < FILTER_SIZE; i++) { for (int j = 0; j < FILTER_SIZE; j++) { for (int k = 0; k < FILTER_SIZE; k++) { // TODO: Play with this! if (matrix.getColorAndCheckBounds(x + i, y + j, z + k).a != 0) { potential += 1.0f; } // Interesting effect: /* float dx = abs(i - 1.5f); float dy = abs(j - 1.5f); float dz = abs(k - 1.5f); if (matrix.getColorAndCheckBounds(x + i, y + j, z + k).a != 0) { if (dx <= 0.75f && dy <= 0.75f && dz <= 0.75f) { potential += 2.0f; } else { potential += 0.75f; } } else if (dx <= 0.75f && dy <= 0.75f && dz <= 0.75f) { potential -= 2.0f; }*/ } } } // Average the samples return potential / (FILTER_SIZE * FILTER_SIZE * FILTER_SIZE); if (matrix.getColor(x, y, z).a != 0.0) { potential += 2.0f; } // Add extra potential from neighbors ( makes it smoother ) // When .a is 0.0, that is an air block if (matrix.getColorAndCheckBounds(x - 1, y, z).a != 0.0) { potential += 0.25f; } if (matrix.getColorAndCheckBounds(x + 1, y, z).a != 0.0) { potential += 0.25f; } if (matrix.getColorAndCheckBounds(x, y - 1, z).a != 0.0) { potential += 0.25f; } if (matrix.getColorAndCheckBounds(x, y + 1, z).a != 0.0) { potential += 0.25f; } if (matrix.getColorAndCheckBounds(x, y, z - 1).a != 0.0) { potential += 0.25f; } if (matrix.getColorAndCheckBounds(x, y, z + 1).a != 0.0) { potential += 0.25f; } // Divide by 7 to average with neighbors a bit return potential / 7.0f; } // This gets color for marching cubes vertices by averaging nearby voxel colors // TODO: Figure out a metter method? color3 ModelMesher::getColor(const f32v3& pos, const VoxelMatrix& matrix) { ui32v3 ipos(glm::round(pos)); if (ipos.x >= matrix.size.x) ipos.x = matrix.size.x - 1; if (ipos.y >= matrix.size.y) ipos.y = matrix.size.y - 1; if (ipos.z >= matrix.size.z) ipos.z = matrix.size.z - 1; ui32 x = ipos.x; ui32 y = ipos.y; ui32 z = ipos.z; int numColors = 0; i32v3 fColor(0); if (y > 0) { if (z > 0) { // Bottom back left if (x > 0) { color4 color = matrix.getColor(x - 1, y - 1, z - 1); if (color.a != 0 && !matrix.isInterior(x - 1, y - 1, z - 1)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } // Bottom back right if (x < matrix.size.x) { color4 color = matrix.getColor(x, y - 1, z - 1); if (color.a != 0 && !matrix.isInterior(x, y - 1, z - 1)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } } if (z < matrix.size.z) { // Bottom front left if (x > 0) { color4 color = matrix.getColor(x - 1, y - 1, z); if (color.a != 0 && !matrix.isInterior(x - 1, y - 1, z)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } // Bottom front right if (x < matrix.size.x) { color4 color = matrix.getColor(x, y - 1, z); if (color.a != 0 && !matrix.isInterior(x, y - 1, z)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } } } if (y < matrix.size.y) { if (z > 0) { // Top back left if (x > 0) { color4 color = matrix.getColor(x - 1, y, z - 1); if (color.a != 0 && !matrix.isInterior(x - 1, y, z - 1)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } // Top back right if (x < matrix.size.x) { color4 color = matrix.getColor(x, y, z - 1); if (color.a != 0 && !matrix.isInterior(x, y, z - 1)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } } if (z < matrix.size.z) { // Top front left if (x > 0) { color4 color = matrix.getColor(x - 1, y, z); if (color.a != 0 && !matrix.isInterior(x - 1, y, z)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } // Top front right if (x < matrix.size.x) { color4 color = matrix.getColor(x, y, z); if (color.a != 0 && !matrix.isInterior(x, y, z)) { numColors++; fColor.r += color.r; fColor.g += color.g; fColor.b += color.b; } } } } if (numColors) { fColor /= numColors; } return color3(fColor.r, fColor.g, fColor.b); } color3 ModelMesher::getColor2(const i32v3& pos, const VoxelMatrix& matrix) { int numColors = 1; i32v3 fColor(0); { // Center color4 vColor = matrix.getColorAndCheckBounds(pos); if (vColor.a != 0 && !matrix.isInterior(pos)) { return vColor.color.rgb; } } { // Left i32v3 vPos = pos + i32v3(-1, 0, 0); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } { // Right i32v3 vPos = pos + i32v3(1, 0, 0); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } { // Bottom i32v3 vPos = pos + i32v3(0, -1, 0); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } { // Top i32v3 vPos = pos + i32v3(0, 1, 0); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } { // Back i32v3 vPos = pos + i32v3(0, 0, -1); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } { // Front i32v3 vPos = pos + i32v3(0, 0, 1); color4 vColor = matrix.getColorAndCheckBounds(vPos); if (vColor.a != 0 && !matrix.isInterior(vPos)) { return vColor.color.rgb; } } return color3(0, 0, 0); if (numColors) { fColor /= numColors; } return color3(fColor.r, fColor.g, fColor.b); } //Macros used to compute gradient vector on each vertex of a cube //argument should be the name of array of vertices //can be verts or *verts if done by reference #define CALC_GRAD_VERT_0(verts) f32v4(points[ind-YtimeZ].w-(verts[1]).w,points[ind-pointsZ].w-(verts[4]).w,points[ind-1].w-(verts[3]).w, (verts[0]).w); #define CALC_GRAD_VERT_1(verts) f32v4((verts[0]).w-points[ind+2*YtimeZ].w,points[ind+YtimeZ-pointsZ].w-(verts[5]).w,points[ind+YtimeZ-1].w-(verts[2]).w, (verts[1]).w); #define CALC_GRAD_VERT_2(verts) f32v4((verts[3]).w-points[ind+2*YtimeZ+1].w,points[ind+YtimeZ-ncellsZ].w-(verts[6]).w,(verts[1]).w-points[ind+YtimeZ+2].w, (verts[2]).w); #define CALC_GRAD_VERT_3(verts) f32v4(points[ind-YtimeZ+1].w-(verts[2]).w,points[ind-ncellsZ].w-(verts[7]).w,(verts[0]).w-points[ind+2].w, (verts[3]).w); #define CALC_GRAD_VERT_4(verts) f32v4(points[ind-YtimeZ+ncellsZ+1].w-(verts[5]).w,(verts[0]).w-points[ind+2*pointsZ].w,points[ind+ncellsZ].w-(verts[7]).w, (verts[4]).w); #define CALC_GRAD_VERT_5(verts) f32v4((verts[4]).w-points[ind+2*YtimeZ+ncellsZ+1].w,(verts[1]).w-points[ind+YtimeZ+2*pointsZ].w,points[ind+YtimeZ+ncellsZ].w-(verts[6]).w, (verts[5]).w); #define CALC_GRAD_VERT_6(verts) f32v4((verts[7]).w-points[ind+2*YtimeZ+ncellsZ+2].w,(verts[2]).w-points[ind+YtimeZ+2*ncellsZ+3].w,(verts[5]).w-points[ind+YtimeZ+ncellsZ+3].w, (verts[6]).w); #define CALC_GRAD_VERT_7(verts) f32v4(points[ind-YtimeZ+ncellsZ+2].w-(verts[6]).w,(verts[3]).w-points[ind+2*ncellsZ+3].w,(verts[4]).w-points[ind+ncellsZ+3].w, (verts[7]).w); /////////////////////////////////////////////////////////////////////////////////////////////////////// // GLOBAL // //Global variables - so they dont have to be passed into functions int pointsZ; //number of points on Z zxis (equal to ncellsZ+1) int YtimeZ; //'plane' of cubes on YZ (equal to (ncellsY+1)*pointsZ ) /////////////////////////////////////////////////////////////////////////////////////////////////////// //Linear Interpolation between two points f32v3 linearInterp(const f32v4& p1, const f32v4& p2, float value) { f32v3 p13(p1); if (fabs(p1.w - p2.w) > 0.00001f) return p13 + (f32v3(p2) - p13) / (p2.w - p1.w)*(value - p1.w); else return p13; } // Source: http://www.angelfire.com/linux/myp/MC/ // With Improvements: http://www.angelfire.com/linux/myp/MCAdvanced/MCImproved.html void ModelMesher::marchingCubes(const VoxelMatrix& matrix, float gradFactorX, float gradFactorY, float gradFactorZ, float minValue, f32v4 * points, std::vector& vertices) { int ncellsX = matrix.size.x; int ncellsY = matrix.size.y; int ncellsZ = matrix.size.z; pointsZ = ncellsZ + 1; //initialize global variable (for extra speed) YtimeZ = (ncellsY + 1)*pointsZ; f32v4 *verts[8]; //vertices of a cube (array of pointers for extra speed) f32v3 intVerts[12]; //linearly interpolated vertices on each edge int cubeIndex; //shows which vertices are outside/inside int edgeIndex; //index returned by edgeTable[cubeIndex] f32v4 gradVerts[8]; //gradients at each vertex of a cube f32v3 grads[12]; //linearly interpolated gradients on each edge int indGrad; //shows which gradients already have been computed int ind, ni, nj; //ind: index of vertex 0 //factor by which corresponding coordinates of gradient vectors are scaled f32v3 factor(1.0f / (2.0*gradFactorX), 1.0f / (2.0*gradFactorY), 1.0f / (2.0*gradFactorZ)); f32v3 mainOffset(-(matrix.size.x / 2.0f), -(matrix.size.y / 2.0f), -(matrix.size.z / 2.0f)); //MAIN LOOP: goes through all the points for (int i = 0; i < ncellsX; i++) { //x axis ni = i*YtimeZ; for (int j = 0; j < ncellsY; j++) { //y axis nj = j*pointsZ; for (int k = 0; k < ncellsZ; k++, ind++) //z axis { //initialize vertices ind = ni + nj + k; verts[0] = &points[ind]; verts[1] = &points[ind + YtimeZ]; verts[4] = &points[ind + pointsZ]; verts[5] = &points[ind + YtimeZ + pointsZ]; verts[2] = &points[ind + YtimeZ + 1]; verts[3] = &points[ind + 1]; verts[6] = &points[ind + YtimeZ + pointsZ + 1]; verts[7] = &points[ind + pointsZ + 1]; //get the index cubeIndex = int(0); for (int n = 0; n < 8; n++) if (verts[n]->w <= minValue) cubeIndex |= (1 << n); //check if its completely inside or outside if (!edgeTable[cubeIndex]) continue; indGrad = int(0); edgeIndex = edgeTable[cubeIndex]; if (edgeIndex & 1) { intVerts[0] = linearInterp(*verts[0], *verts[1], minValue); if (i != 0 && j != 0 && k != 0) gradVerts[0] = CALC_GRAD_VERT_0(*verts) else gradVerts[0] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[1] = CALC_GRAD_VERT_1(*verts) else gradVerts[1] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 3; grads[0] = linearInterp(gradVerts[0], gradVerts[1], minValue); grads[0].x *= factor.x; grads[0].y *= factor.y; grads[0].z *= factor.z; } if (edgeIndex & 2) { intVerts[1] = linearInterp(*verts[1], *verts[2], minValue); if (!(indGrad & 2)) { if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[1] = CALC_GRAD_VERT_1(*verts) else gradVerts[1] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 2; } if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[2] = CALC_GRAD_VERT_2(*verts) else gradVerts[2] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 4; grads[1] = linearInterp(gradVerts[1], gradVerts[2], minValue); grads[1].x *= factor.x; grads[1].y *= factor.y; grads[1].z *= factor.z; } if (edgeIndex & 4) { intVerts[2] = linearInterp(*verts[2], *verts[3], minValue); if (!(indGrad & 4)) { if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[2] = CALC_GRAD_VERT_2(*verts) else gradVerts[2] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 4; } if (i != 0 && j != 0 && k != ncellsZ - 1) gradVerts[3] = CALC_GRAD_VERT_3(*verts) else gradVerts[3] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 8; grads[2] = linearInterp(gradVerts[2], gradVerts[3], minValue); grads[2].x *= factor.x; grads[2].y *= factor.y; grads[2].z *= factor.z; } if (edgeIndex & 8) { intVerts[3] = linearInterp(*verts[3], *verts[0], minValue); if (!(indGrad & 8)) { if (i != 0 && j != 0 && k != ncellsZ - 1) gradVerts[3] = CALC_GRAD_VERT_3(*verts) else gradVerts[3] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 8; } if (!(indGrad & 1)) { if (i != 0 && j != 0 && k != 0) gradVerts[0] = CALC_GRAD_VERT_0(*verts) else gradVerts[0] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 1; } grads[3] = linearInterp(gradVerts[3], gradVerts[0], minValue); grads[3].x *= factor.x; grads[3].y *= factor.y; grads[3].z *= factor.z; } if (edgeIndex & 16) { intVerts[4] = linearInterp(*verts[4], *verts[5], minValue); if (i != 0 && j != ncellsY - 1 && k != 0) gradVerts[4] = CALC_GRAD_VERT_4(*verts) else gradVerts[4] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); if (i != ncellsX - 1 && j != ncellsY - 1 && k != 0) gradVerts[5] = CALC_GRAD_VERT_5(*verts) else gradVerts[5] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 48; grads[4] = linearInterp(gradVerts[4], gradVerts[5], minValue); grads[4].x *= factor.x; grads[4].y *= factor.y; grads[4].z *= factor.z; } if (edgeIndex & 32) { intVerts[5] = linearInterp(*verts[5], *verts[6], minValue); if (!(indGrad & 32)) { if (i != ncellsX - 1 && j != ncellsY - 1 && k != 0) gradVerts[5] = CALC_GRAD_VERT_5(*verts) else gradVerts[5] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 32; } if (i != ncellsX - 1 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[6] = CALC_GRAD_VERT_6(*verts) else gradVerts[6] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 64; grads[5] = linearInterp(gradVerts[5], gradVerts[6], minValue); grads[5].x *= factor.x; grads[5].y *= factor.y; grads[5].z *= factor.z; } if (edgeIndex & 64) { intVerts[6] = linearInterp(*verts[6], *verts[7], minValue); if (!(indGrad & 64)) { if (i != ncellsX - 1 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[6] = CALC_GRAD_VERT_6(*verts) else gradVerts[6] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 64; } if (i != 0 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[7] = CALC_GRAD_VERT_7(*verts) else gradVerts[7] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 128; grads[6] = linearInterp(gradVerts[6], gradVerts[7], minValue); grads[6].x *= factor.x; grads[6].y *= factor.y; grads[6].z *= factor.z; } if (edgeIndex & 128) { intVerts[7] = linearInterp(*verts[7], *verts[4], minValue); if (!(indGrad & 128)) { if (i != 0 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[7] = CALC_GRAD_VERT_7(*verts) else gradVerts[7] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 128; } if (!(indGrad & 16)) { if (i != 0 && j != ncellsY - 1 && k != 0) gradVerts[4] = CALC_GRAD_VERT_4(*verts) else gradVerts[4] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 16; } grads[7] = linearInterp(gradVerts[7], gradVerts[4], minValue); grads[7].x *= factor.x; grads[7].y *= factor.y; grads[7].z *= factor.z; } if (edgeIndex & 256) { intVerts[8] = linearInterp(*verts[0], *verts[4], minValue); if (!(indGrad & 1)) { if (i != 0 && j != 0 && k != 0) gradVerts[0] = CALC_GRAD_VERT_0(*verts) else gradVerts[0] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 1; } if (!(indGrad & 16)) { if (i != 0 && j != ncellsY - 1 && k != 0) gradVerts[4] = CALC_GRAD_VERT_4(*verts) else gradVerts[4] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 16; } grads[8] = linearInterp(gradVerts[0], gradVerts[4], minValue); grads[8].x *= factor.x; grads[8].y *= factor.y; grads[8].z *= factor.z; } if (edgeIndex & 512) { intVerts[9] = linearInterp(*verts[1], *verts[5], minValue); if (!(indGrad & 2)) { if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[1] = CALC_GRAD_VERT_1(*verts) else gradVerts[1] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 2; } if (!(indGrad & 32)) { if (i != ncellsX - 1 && j != ncellsY - 1 && k != 0) gradVerts[5] = CALC_GRAD_VERT_5(*verts) else gradVerts[5] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 32; } grads[9] = linearInterp(gradVerts[1], gradVerts[5], minValue); grads[9].x *= factor.x; grads[9].y *= factor.y; grads[9].z *= factor.z; } if (edgeIndex & 1024) { intVerts[10] = linearInterp(*verts[2], *verts[6], minValue); if (!(indGrad & 4)) { if (i != ncellsX - 1 && j != 0 && k != 0) gradVerts[2] = CALC_GRAD_VERT_2(*verts) else gradVerts[5] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 4; } if (!(indGrad & 64)) { if (i != ncellsX - 1 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[6] = CALC_GRAD_VERT_6(*verts) else gradVerts[6] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 64; } grads[10] = linearInterp(gradVerts[2], gradVerts[6], minValue); grads[10].x *= factor.x; grads[10].y *= factor.y; grads[10].z *= factor.z; } if (edgeIndex & 2048) { intVerts[11] = linearInterp(*verts[3], *verts[7], minValue); if (!(indGrad & 8)) { if (i != 0 && j != 0 && k != ncellsZ - 1) gradVerts[3] = CALC_GRAD_VERT_3(*verts) else gradVerts[3] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 8; } if (!(indGrad & 128)) { if (i != 0 && j != ncellsY - 1 && k != ncellsZ - 1) gradVerts[7] = CALC_GRAD_VERT_7(*verts) else gradVerts[7] = f32v4(1.0f, 1.0f, 1.0f, 1.0f); indGrad |= 128; } grads[11] = linearInterp(gradVerts[3], gradVerts[7], minValue); grads[11].x *= factor.x; grads[11].y *= factor.y; grads[11].z *= factor.z; } //now build the triangles using triTable for (int n = 0; triTable[cubeIndex][n] != -1; n += 3) { int index[3] = { triTable[cubeIndex][n + 2], triTable[cubeIndex][n + 1], triTable[cubeIndex][n] }; int startVertex = vertices.size(); vertices.resize(vertices.size() + 3); for (int h = 0; h < 3; h++) { vertices[startVertex + h].color = getColor(intVerts[index[h]], matrix); vertices[startVertex + h].pos = intVerts[index[h]] + mainOffset; vertices[startVertex + h].normal = grads[index[h]]; } } } } } } void ModelMesher::genMatrixMesh(const VoxelMatrix& matrix, std::vector& vertices, std::vector& indices) { // TODO(Ben): Could be optimized f32v3 mainOffset(matrix.size.x / 2.0f, matrix.size.y / 2.0f, matrix.size.z / 2.0f); int voxelIndex; for(ui32 z = 0; z < matrix.size.z; z++) { for (ui32 y = 0; y < matrix.size.y; y++) { for (ui32 x = 0; x < matrix.size.x; x++) { voxelIndex = matrix.getIndex(x, y, z); const ColorRGBA8& voxel = matrix.getColor(voxelIndex); // Get the current voxel's color if(voxel.a == 0) continue; // If the current voxel is invisible go to next voxel f32v3 offset = f32v3(x, y, z) - mainOffset; // Position of the current voxel in the model for (int face = 0; face < 6; face++) { // For each face of the voxel if(matrix.getColorAndCheckBounds(i32v3(x, y, z) + VOXEL_SIDES[face]).a == 0) { // Check if the adjacent voxel is invisible int indexStart = (int)vertices.size(); int indiceStart = (int)indices.size(); // Add the 4 vertices for this face vertices.resize(indexStart + 4); for (int l = 0; l < 4; l++) { vertices[indexStart + l].pos = offset + VOXEL_MODEL[face * 4 + l]; vertices[indexStart + l].color = voxel.color.rgb; vertices[indexStart + l].normal = f32v3(VOXEL_SIDES[face]); } // Add the 6 indices for this face indices.resize(indiceStart + 6); indices[indiceStart] = indexStart + VOXEL_INDICES[0]; indices[indiceStart + 1] = indexStart + VOXEL_INDICES[1]; indices[indiceStart + 2] = indexStart + VOXEL_INDICES[2]; indices[indiceStart + 3] = indexStart + VOXEL_INDICES[3]; indices[indiceStart + 4] = indexStart + VOXEL_INDICES[4]; indices[indiceStart + 5] = indexStart + VOXEL_INDICES[5]; } } } } } } ================================================ FILE: SoA/ModelMesher.h ================================================ #pragma once #ifndef ModelMesher_h__ #define ModelMesher_h__ #include class VoxelMatrix; class VoxelModel; class VoxelModelMesh; class VoxelModelVertex; class ModelMesher { public: static VoxelModelMesh createMesh(const VoxelModel* model); static VoxelModelMesh createMarchingCubesMesh(const VoxelModel* model); static VoxelModelMesh createDualContouringMesh(const VoxelModel* model); private: // *** Regular *** static void genMatrixMesh(const VoxelMatrix& matrix, std::vector& vertices, std::vector& indices); // *** Marching Cubes *** static color3 getColor(const f32v3& pos, const VoxelMatrix& matrix); static color3 getColor2(const i32v3& pos, const VoxelMatrix& matrix); static void marchingCubes(const VoxelMatrix& matrix, float gradFactorX, float gradFactorY, float gradFactorZ, float minValue, f32v4 * points, std::vector& vertices); static f32 getMarchingPotential(const VoxelMatrix& matrix, int x, int y, int z); }; #endif //ModelMesher_h__ ================================================ FILE: SoA/MusicPlayer.cpp ================================================ #include "stdafx.h" #include "MusicPlayer.h" #include #include #include "SoaFileSystem.h" void MusicPlayer::refreshLists(const SoaFileSystem& fs) { // Kill current lists m_music.clear(); m_playlists.clear(); // Check for the music folder const vpath& musicPath = fs.get("Music").getSearchDirectory(); if (!musicPath.isValid()) { // Create the folder and then exit vio::buildDirectoryTree(musicPath); return; } vdir musicDir; if (!musicPath.asDirectory(&musicDir)) { // What happened here... return; } searchTree(musicDir); } void MusicPlayer::begin(vsound::Engine& engine VORB_MAYBE_UNUSED) { if (m_isRunning) return; m_isRunning = true; } void MusicPlayer::stop() { if (!m_isRunning) return; m_isRunning = false; } inline bool strEndsWith(const nString& value, const nString& ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } void MusicPlayer::searchTree(const vdir& root) { root.forEachEntry([&] (Sender s VORB_MAYBE_UNUSED, const vpath& p) { if (p.isFile()) { nString fileName = p.getLeaf(); if (strEndsWith(fileName, ".mp3")) { m_music.push_back(p); printf("Found Music: %s\n", fileName.c_str()); } else if (strEndsWith(fileName, ".ogg")) { m_music.push_back(p); printf("Found Music: %s\n", fileName.c_str()); } else if (strEndsWith(fileName, ".playlist.yml")) { m_playlists.push_back(p); printf("Found Playlist: %s\n", fileName.c_str()); } else { // Not a valid file } } else if (p.isDirectory()) { vdir child; p.asDirectory(&child); searchTree(child); } }); } ================================================ FILE: SoA/MusicPlayer.h ================================================ /// /// MusicPlayer.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Will play music files from a playlist /// TODO: Not finished yet /// #pragma once #ifndef MusicPlayer_h__ #define MusicPlayer_h__ #include #include class SoaFileSystem; DECL_VSOUND(class Engine) class MusicPlayer { public: void refreshLists(const SoaFileSystem& fs); void begin(vsound::Engine& engine); void stop(); private: void searchTree(const vdir& path); bool m_isRunning = false; std::vector m_music; std::vector m_playlists; }; #endif // MusicPlayer_h__ ================================================ FILE: SoA/NightVisionRenderStage.cpp ================================================ #include "stdafx.h" #include "NightVisionRenderStage.h" #include #include #include #include #include #include #include "ShaderLoader.h" #include "SoaOptions.h" KEG_TYPE_DEF_SAME_NAME(NightVisionRenderParams, kt) { using namespace keg; kt.addValue("Color", Value::basic(offsetof(NightVisionRenderParams, color), BasicType::F32_V3)); kt.addValue("Contrast", Value::basic(offsetof(NightVisionRenderParams, luminanceExponent), BasicType::F32)); kt.addValue("Filter", Value::basic(offsetof(NightVisionRenderParams, luminanceTare), BasicType::F32)); kt.addValue("Brightness", Value::basic(offsetof(NightVisionRenderParams, colorAmplification), BasicType::F32)); kt.addValue("Noise", Value::basic(offsetof(NightVisionRenderParams, noisePower), BasicType::F32)); kt.addValue("ColorNoise", Value::basic(offsetof(NightVisionRenderParams, noiseColor), BasicType::F32)); } NightVisionRenderParams NightVisionRenderParams::createDefault() { NightVisionRenderParams v = {}; v.colorAmplification = NIGHT_VISION_DEFAULT_COLOR_AMPLIFICATION; v.luminanceExponent = NIGHT_VISION_DEFAULT_LUMINANCE_EXPONENT; v.luminanceTare = NIGHT_VISION_DEFAULT_LUMINANCE_TARE; v.noisePower = NIGHT_VISION_DEFAULT_NOISE_POWER; v.noiseColor = NIGHT_VISION_DEFAULT_NOISE_COLOR; v.color = NIGHT_VISION_DEFAULT_VISION_COLOR; return v; } void NightVisionRenderStage::hook(vg::FullQuadVBO* quad) { m_quad = quad; m_texNoise.width = NIGHT_VISION_NOISE_QUALITY; m_texNoise.height = NIGHT_VISION_NOISE_QUALITY; // Generate random data i32 pixCount = m_texNoise.width * m_texNoise.height; ui8* data = new ui8[pixCount]; Random r(clock()); for (i32 i = 0; i < pixCount; i++) { data[i] = (ui8)(r.genMT() * 255.0f); } // Build noise texture glGenTextures(1, &m_texNoise.id); glBindTexture(GL_TEXTURE_2D, m_texNoise.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_texNoise.width, m_texNoise.height, 0, GL_RED, GL_UNSIGNED_BYTE, data); vg::SamplerState::POINT_WRAP.set(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); delete[] data; } void NightVisionRenderStage::setParams(const NightVisionRenderParams& params) { m_program.use(); glUniform1f(m_program.getUniform("unLuminanceExponent"), params.luminanceExponent); glUniform1f(m_program.getUniform("unLuminanceTare"), params.luminanceTare); glUniform1f(m_program.getUniform("unNoisePower"), params.noisePower); glUniform1f(m_program.getUniform("unNoiseColor"), params.noiseColor); glUniform1f(m_program.getUniform("unColorAmplification"), params.colorAmplification); glUniform3f(m_program.getUniform("unVisionColor"), params.color.r, params.color.g, params.color.b); } void NightVisionRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { if (m_texNoise.id) { glDeleteTextures(1, &m_texNoise.id); m_texNoise.id = 0; } } void NightVisionRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED /*= nullptr*/) { m_et += NIGHT_VISION_DEFAULT_NOISE_TIME_STEP; //_visionColorHSL.r = fmod(_visionColorHSL.r = 0.005f, 6.28f); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_texNoise.id); if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/NightVision.frag"); m_program.use(); glUniform1i(m_program.getUniform("unTexColor"), NIGHT_VISION_TEXTURE_SLOT_COLOR); glUniform1i(m_program.getUniform("unTexNoise"), NIGHT_VISION_TEXTURE_SLOT_NOISE); setParams(NightVisionRenderParams::createDefault()); } else { m_program.use(); } m_program.enableVertexAttribArrays(); glUniform1f(m_program.getUniform("unTime"), m_et); glDisable(GL_DEPTH_TEST); m_quad->draw(); glEnable(GL_DEPTH_TEST); m_program.disableVertexAttribArrays(); vg::GLProgram::unuse(); } ================================================ FILE: SoA/NightVisionRenderStage.h ================================================ /// /// NightVisionRenderStage.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 6 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Night vision effect /// #pragma once #ifndef NightVisionRenderStage_h__ #define NightVisionRenderStage_h__ #define NIGHT_VISION_NOISE_QUALITY 512 #define NIGHT_VISION_DEFAULT_NOISE_POWER 0.001f #define NIGHT_VISION_DEFAULT_NOISE_COLOR 0.2f #define NIGHT_VISION_DEFAULT_LUMINANCE_TARE 0.5f #define NIGHT_VISION_DEFAULT_LUMINANCE_EXPONENT 1.15f #define NIGHT_VISION_DEFAULT_COLOR_AMPLIFICATION 5.0f #define NIGHT_VISION_DEFAULT_NOISE_TIME_STEP 0.016667f #define NIGHT_VISION_TEXTURE_SLOT_COLOR 0 #define NIGHT_VISION_TEXTURE_SLOT_NOISE 1 #define NIGHT_VISION_DEFAULT_VISION_COLOR f32v3(0.1f, 0.95f, 0.2f) #include #include #include #include #include "IRenderStage.h" class NightVisionRenderParams { public: static NightVisionRenderParams createDefault(); f32v3 color; f32 luminanceExponent; f32 luminanceTare; f32 colorAmplification; f32 noisePower; f32 noiseColor; }; KEG_TYPE_DECL(NightVisionRenderParams); /// Renders a night vision post-process effect class NightVisionRenderStage : public IRenderStage { public: void hook(vg::FullQuadVBO* quad); void setParams(const NightVisionRenderParams& params); /// Disposes and deletes the shader and turns off visibility /// If stage does lazy init, shader will reload at next draw virtual void dispose(StaticLoadContext& context) override; /// Draws the render stage virtual void render(const Camera* camera = nullptr) override; private: vg::GLProgram m_program; vg::FullQuadVBO* m_quad; ///< For use in processing through data vg::Texture m_texNoise; ///< A noise texture for blurry static f32 m_et = 0.0f; ///< Counter for elapsed total time f32v3 m_visionColorHSL = NIGHT_VISION_DEFAULT_VISION_COLOR; ///< Color of night vision }; #endif // NightVisionRenderStage_h__ ================================================ FILE: SoA/Noise.cpp ================================================ #include "stdafx.h" #include "Noise.h" #include KEG_TYPE_DEF_SAME_NAME(NoiseBase, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, NoiseBase, base, F64); kt.addValue("funcs", keg::Value::array(offsetof(NoiseBase, funcs), keg::Value::custom(0, "TerrainFuncProperties", false))); } KEG_ENUM_DEF(TerrainStage, TerrainStage, kt) { kt.addValue("noise", TerrainStage::NOISE); kt.addValue("squared", TerrainStage::SQUARED); kt.addValue("cubed", TerrainStage::CUBED); kt.addValue("noise_ridged", TerrainStage::RIDGED_NOISE); kt.addValue("noise_abs", TerrainStage::ABS_NOISE); kt.addValue("noise_squared", TerrainStage::SQUARED_NOISE); kt.addValue("noise_cubed", TerrainStage::CUBED_NOISE); kt.addValue("noise_cellular", TerrainStage::CELLULAR_NOISE); kt.addValue("noise_cellular_squared", TerrainStage::CELLULAR_SQUARED_NOISE); kt.addValue("noise_cellular_cubed", TerrainStage::CELLULAR_CUBED_NOISE); kt.addValue("constant", TerrainStage::CONSTANT); kt.addValue("passthrough", TerrainStage::PASS_THROUGH); } KEG_ENUM_DEF(TerrainOp, TerrainOp, kt) { kt.addValue("add", TerrainOp::ADD); kt.addValue("sub", TerrainOp::SUB); kt.addValue("mul", TerrainOp::MUL); kt.addValue("div", TerrainOp::DIV); } KEG_TYPE_DEF_SAME_NAME(TerrainFuncProperties, kt) { kt.addValue("type", keg::Value::custom(offsetof(TerrainFuncProperties, func), "TerrainStage", true)); kt.addValue("op", keg::Value::custom(offsetof(TerrainFuncProperties, op), "TerrainOp", true)); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, octaves, I32); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, persistence, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, frequency, F64); kt.addValue("val", keg::Value::basic(offsetof(TerrainFuncProperties, low), keg::BasicType::F64)); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, low, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, high, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainFuncProperties, clamp, F64_V2); kt.addValue("children", keg::Value::array(offsetof(TerrainFuncProperties, children), keg::Value::custom(0, "TerrainFuncProperties", false))); } // // Description : Array and textureless 2D/3D/4D simplex // noise functions. // Author : Ian McEwan, Ashima Arts. // Maintainer : ijm // Lastmod : 20110822 (ijm) // License : Copyright (C) 2011 Ashima Arts. MIT License. // Distributed under the MIT License. See LICENSE file. // https://github.com/ashima/webgl-noise // // Converted to C++ by Ben Arnold // Permutation polynomial: (34x^2 + x) mod 289 inline f64v3 permute(const f64v3& x) { return glm::mod((34.0 * x + 1.0) * x, 289.0); } // TODO(Ben): Fastfloor? f64v2 Noise::cellular(const f64v3& P) { #define K 0.142857142857 // 1/7 #define Ko 0.428571428571 // 1/2-K/2 #define K2 0.020408163265306 // 1/(7*7) #define Kz 0.166666666667 // 1/6 #define Kzo 0.416666666667 // 1/2-1/6*2 #define jitter 1.0 // smaller jitter gives more regular pattern f64v3 Pi = glm::mod(glm::floor(P), 289.0); f64v3 Pf = glm::fract(P) - 0.5; f64v3 Pfx = Pf.x + f64v3(1.0, 0.0, -1.0); f64v3 Pfy = Pf.y + f64v3(1.0, 0.0, -1.0); f64v3 Pfz = Pf.z + f64v3(1.0, 0.0, -1.0); f64v3 p = permute(Pi.x + f64v3(-1.0, 0.0, 1.0)); f64v3 p1 = permute(p + Pi.y - 1.0); f64v3 p2 = permute(p + Pi.y); f64v3 p3 = permute(p + Pi.y + 1.0); f64v3 p11 = permute(p1 + Pi.z - 1.0); f64v3 p12 = permute(p1 + Pi.z); f64v3 p13 = permute(p1 + Pi.z + 1.0); f64v3 p21 = permute(p2 + Pi.z - 1.0); f64v3 p22 = permute(p2 + Pi.z); f64v3 p23 = permute(p2 + Pi.z + 1.0); f64v3 p31 = permute(p3 + Pi.z - 1.0); f64v3 p32 = permute(p3 + Pi.z); f64v3 p33 = permute(p3 + Pi.z + 1.0); f64v3 ox11 = glm::fract(p11*K) - Ko; f64v3 oy11 = glm::mod(glm::floor(p11*K), 7.0)*K - Ko; f64v3 oz11 = glm::floor(p11*K2)*Kz - Kzo; // p11 < 289 guaranteed f64v3 ox12 = glm::fract(p12*K) - Ko; f64v3 oy12 = glm::mod(glm::floor(p12*K), 7.0)*K - Ko; f64v3 oz12 = glm::floor(p12*K2)*Kz - Kzo; f64v3 ox13 = glm::fract(p13*K) - Ko; f64v3 oy13 = glm::mod(glm::floor(p13*K), 7.0)*K - Ko; f64v3 oz13 = glm::floor(p13*K2)*Kz - Kzo; f64v3 ox21 = glm::fract(p21*K) - Ko; f64v3 oy21 = glm::mod(glm::floor(p21*K), 7.0)*K - Ko; f64v3 oz21 = glm::floor(p21*K2)*Kz - Kzo; f64v3 ox22 = glm::fract(p22*K) - Ko; f64v3 oy22 = glm::mod(glm::floor(p22*K), 7.0)*K - Ko; f64v3 oz22 = glm::floor(p22*K2)*Kz - Kzo; f64v3 ox23 = glm::fract(p23*K) - Ko; f64v3 oy23 = glm::mod(glm::floor(p23*K), 7.0)*K - Ko; f64v3 oz23 = glm::floor(p23*K2)*Kz - Kzo; f64v3 ox31 = glm::fract(p31*K) - Ko; f64v3 oy31 = glm::mod(glm::floor(p31*K), 7.0)*K - Ko; f64v3 oz31 = glm::floor(p31*K2)*Kz - Kzo; f64v3 ox32 = glm::fract(p32*K) - Ko; f64v3 oy32 = glm::mod(glm::floor(p32*K), 7.0)*K - Ko; f64v3 oz32 = glm::floor(p32*K2)*Kz - Kzo; f64v3 ox33 = glm::fract(p33*K) - Ko; f64v3 oy33 = glm::mod(glm::floor(p33*K), 7.0)*K - Ko; f64v3 oz33 = glm::floor(p33*K2)*Kz - Kzo; f64v3 dx11 = Pfx + jitter*ox11; f64v3 dy11 = Pfy.x + jitter*oy11; f64v3 dz11 = Pfz.x + jitter*oz11; f64v3 dx12 = Pfx + jitter*ox12; f64v3 dy12 = Pfy.x + jitter*oy12; f64v3 dz12 = Pfz.y + jitter*oz12; f64v3 dx13 = Pfx + jitter*ox13; f64v3 dy13 = Pfy.x + jitter*oy13; f64v3 dz13 = Pfz.z + jitter*oz13; f64v3 dx21 = Pfx + jitter*ox21; f64v3 dy21 = Pfy.y + jitter*oy21; f64v3 dz21 = Pfz.x + jitter*oz21; f64v3 dx22 = Pfx + jitter*ox22; f64v3 dy22 = Pfy.y + jitter*oy22; f64v3 dz22 = Pfz.y + jitter*oz22; f64v3 dx23 = Pfx + jitter*ox23; f64v3 dy23 = Pfy.y + jitter*oy23; f64v3 dz23 = Pfz.z + jitter*oz23; f64v3 dx31 = Pfx + jitter*ox31; f64v3 dy31 = Pfy.z + jitter*oy31; f64v3 dz31 = Pfz.x + jitter*oz31; f64v3 dx32 = Pfx + jitter*ox32; f64v3 dy32 = Pfy.z + jitter*oy32; f64v3 dz32 = Pfz.y + jitter*oz32; f64v3 dx33 = Pfx + jitter*ox33; f64v3 dy33 = Pfy.z + jitter*oy33; f64v3 dz33 = Pfz.z + jitter*oz33; f64v3 d11 = dx11 * dx11 + dy11 * dy11 + dz11 * dz11; f64v3 d12 = dx12 * dx12 + dy12 * dy12 + dz12 * dz12; f64v3 d13 = dx13 * dx13 + dy13 * dy13 + dz13 * dz13; f64v3 d21 = dx21 * dx21 + dy21 * dy21 + dz21 * dz21; f64v3 d22 = dx22 * dx22 + dy22 * dy22 + dz22 * dz22; f64v3 d23 = dx23 * dx23 + dy23 * dy23 + dz23 * dz23; f64v3 d31 = dx31 * dx31 + dy31 * dy31 + dz31 * dz31; f64v3 d32 = dx32 * dx32 + dy32 * dy32 + dz32 * dz32; f64v3 d33 = dx33 * dx33 + dy33 * dy33 + dz33 * dz33; // Sort out the two smallest distances (F1, F2) #if 0 // Cheat and sort out only F1 f64v3 d1 = glm::min(glm::min(d11, d12), d13); f64v3 d2 = glm::min(glm::min(d21, d22), d23); f64v3 d3 = glm::min(glm::min(d31, d32), d33); f64v3 d = glm::min(glm::min(d1, d2), d3); d.x = glm::min(glm::min(d.x, d.y), d.z); return glm::sqrt(d.xx); // F1 duplicated, no F2 computed #else // Do it right and sort out both F1 and F2 f64v3 d1a = glm::min(d11, d12); d12 = glm::max(d11, d12); d11 = glm::min(d1a, d13); // Smallest now not in d12 or d13 d13 = glm::max(d1a, d13); d12 = glm::min(d12, d13); // 2nd smallest now not in d13 f64v3 d2a = glm::min(d21, d22); d22 = glm::max(d21, d22); d21 = glm::min(d2a, d23); // Smallest now not in d22 or d23 d23 = glm::max(d2a, d23); d22 = glm::min(d22, d23); // 2nd smallest now not in d23 f64v3 d3a = glm::min(d31, d32); d32 = glm::max(d31, d32); d31 = glm::min(d3a, d33); // Smallest now not in d32 or d33 d33 = glm::max(d3a, d33); d32 = glm::min(d32, d33); // 2nd smallest now not in d33 f64v3 da = glm::min(d11, d21); d21 = glm::max(d11, d21); d11 = glm::min(da, d31); // Smallest now in d11 d31 = glm::max(da, d31); // 2nd smallest now not in d31 d11 = (d11.x < d11.y) ? d11 : f64v3(d11.y, d11.x, d11.z); d11 = (d11.x < d11.z) ? d11 : f64v3(d11.z, d11.y, d11.x); d12 = glm::min(d12, d21); // 2nd smallest now not in d21 d12 = glm::min(d12, d22); // nor in d22 d12 = glm::min(d12, d31); // nor in d31 d12 = glm::min(d12, d32); // nor in d32 d11 = f64v3(d11.x, glm::min(f64v2(d11.y, d11.z), f64v2(d12.x, d12.y))); // nor in d12.yz d11.y = glm::min(d11.y, d12.z); // Only two more to go d11.y = glm::min(d11.y, d11.z); // Done! (Phew!) return glm::sqrt(f64v2(d11.x, d11.y)); // F1, F2 #endif } // Multi-octave Simplex noise // For each octave, a higher frequency/lower amplitude function will be added to the original. // The higher the persistence [0-1], the more of each succeeding octave will be added. //SOURCE // http://www.6by9.net/simplex-noise-for-c-and-python/ #define offsetfmult 1.45 inline int fastfloor(const f64 x) { return x > 0 ? (int)x : (int)x - 1; } inline f64 dot(const f64* g, const f64 x, const f64 y) { return g[0] * x + g[1] * y; } inline f64 dot(const f64* g, const f64 x, const f64 y, const f64 z) { return g[0] * x + g[1] * y + g[2] * z; } inline f64 dot(const f64* g, const f64 x, const f64 y, const f64 z, const f64 w) { return g[0] * x + g[1] * y + g[2] * z + g[3] * w; } f64 Noise::fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y) { f64 total = 0.0; f64 frequency = freq; f64 amplitude = 1.0; // We have to keep track of the largest possible amplitude, // because each octave adds more, and we need a value in [-1, 1]. f64 maxAmplitude = 0.0; for (int i = 0; i < octaves; i++) { total += raw(x * frequency, y * frequency) * amplitude; frequency *= 2.0; maxAmplitude += amplitude; amplitude *= persistence; } return total / maxAmplitude; } f64 Noise::fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y, const f64 z) { f64 total = 0.0; f64 frequency = freq; f64 amplitude = 1.0; // We have to keep track of the largest possible amplitude, // because each octave adds more, and we need a value in [-1, 1]. f64 maxAmplitude = 0.0; for (int i = 0; i < octaves; i++) { total += raw(x * frequency, y * frequency, z * frequency) * amplitude; frequency *= 2.0; maxAmplitude += amplitude; amplitude *= persistence; } return total / maxAmplitude; } f64 Noise::fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y, const f64 z, const f64 w) { f64 total = 0; f64 frequency = freq; f64 amplitude = 1; // We have to keep track of the largest possible amplitude, // because each octave adds more, and we need a value in [-1, 1]. f64 maxAmplitude = 0; for (int i = 0; i < octaves; i++) { total += raw(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude; frequency *= 2.0; maxAmplitude += amplitude; amplitude *= persistence; } return total / maxAmplitude; } // 2D raw Simplex noise f64 Noise::raw(const f64 x, const f64 y) { // Noise contributions from the three corners f64 n0, n1, n2; // Skew the input space to determine which simplex cell we're in f64 F2 = 0.5 * (sqrtf(3.0) - 1.0); // Hairy factor for 2D f64 s = (x + y) * F2; int i = fastFloor(x + s); int j = fastFloor(y + s); f64 G2 = (3.0 - sqrtf(3.0)) / 6.0; f64 t = (i + j) * G2; // Unskew the cell origin back to (x,y) space f64 X0 = i - t; f64 Y0 = j - t; // The x,y distances from the cell origin f64 x0 = x - X0; f64 y0 = y - Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords if (x0>y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) else { i1 = 0; j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 f64 x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords f64 y1 = y0 - j1 + G2; f64 x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords f64 y2 = y0 - 1.0 + 2.0 * G2; // Work out the hashed gradient indices of the three simplex corners int ii = i & 255; int jj = j & 255; int gi0 = perm[ii + perm[jj]] % 12; int gi1 = perm[ii + i1 + perm[jj + j1]] % 12; int gi2 = perm[ii + 1 + perm[jj + 1]] % 12; // Calculate the contribution from the three corners f64 t0 = 0.5 - x0*x0 - y0*y0; if (t0<0) n0 = 0.0; else { t0 *= t0; n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient } f64 t1 = 0.5 - x1*x1 - y1*y1; if (t1<0) n1 = 0.0; else { t1 *= t1; n1 = t1 * t1 * dot(grad3[gi1], x1, y1); } f64 t2 = 0.5 - x2*x2 - y2*y2; if (t2<0) n2 = 0.0; else { t2 *= t2; n2 = t2 * t2 * dot(grad3[gi2], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 70.0 * (n0 + n1 + n2); } // 3D raw Simplex noise f64 Noise::raw(const f64 x, const f64 y, const f64 z) { f64 n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in const f64 F3 = 1.0 / 3.0; f64 s = (x + y + z)*F3; // Very nice and simple skew factor for 3D int i = fastFloor(x + s); int j = fastFloor(y + s); int k = fastFloor(z + s); const f64 G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too f64 t = (i + j + k)*G3; f64 X0 = i - t; // Unskew the cell origin back to (x,y,z) space f64 Y0 = j - t; f64 Z0 = k - t; f64 x0 = x - X0; // The x,y,z distances from the cell origin f64 y0 = y - Y0; f64 z0 = z - Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords if (x0 >= y0) { if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order } else { // x0 y0) ? 32 : 0; int c2 = (x0 > z0) ? 16 : 0; int c3 = (y0 > z0) ? 8 : 0; int c4 = (x0 > w0) ? 4 : 0; int c5 = (y0 > w0) ? 2 : 0; int c6 = (z0 > w0) ? 1 : 0; int c = c1 + c2 + c3 + c4 + c5 + c6; int i1, j1, k1, l1; // The integer offsets for the second simplex corner int i2, j2, k2, l2; // The integer offsets for the third simplex corner int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; j1 = simplex[c][1] >= 3 ? 1 : 0; k1 = simplex[c][2] >= 3 ? 1 : 0; l1 = simplex[c][3] >= 3 ? 1 : 0; // The number 2 in the "simplex" array is at the second largest coordinate. i2 = simplex[c][0] >= 2 ? 1 : 0; j2 = simplex[c][1] >= 2 ? 1 : 0; k2 = simplex[c][2] >= 2 ? 1 : 0; l2 = simplex[c][3] >= 2 ? 1 : 0; // The number 1 in the "simplex" array is at the second smallest coordinate. i3 = simplex[c][0] >= 1 ? 1 : 0; j3 = simplex[c][1] >= 1 ? 1 : 0; k3 = simplex[c][2] >= 1 ? 1 : 0; l3 = simplex[c][3] >= 1 ? 1 : 0; // The fifth corner has all coordinate offsets = 1, so no need to look that up. f64 x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords f64 y1 = y0 - j1 + G4; f64 z1 = z0 - k1 + G4; f64 w1 = w0 - l1 + G4; f64 x2 = x0 - i2 + 2.0*G4; // Offsets for third corner in (x,y,z,w) coords f64 y2 = y0 - j2 + 2.0*G4; f64 z2 = z0 - k2 + 2.0*G4; f64 w2 = w0 - l2 + 2.0*G4; f64 x3 = x0 - i3 + 3.0*G4; // Offsets for fourth corner in (x,y,z,w) coords f64 y3 = y0 - j3 + 3.0*G4; f64 z3 = z0 - k3 + 3.0*G4; f64 w3 = w0 - l3 + 3.0*G4; f64 x4 = x0 - 1.0 + 4.0*G4; // Offsets for last corner in (x,y,z,w) coords f64 y4 = y0 - 1.0 + 4.0*G4; f64 z4 = z0 - 1.0 + 4.0*G4; f64 w4 = w0 - 1.0 + 4.0*G4; // Work out the hashed gradient indices of the five simplex corners int ii = i & 255; int jj = j & 255; int kk = k & 255; int ll = l & 255; int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32; int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32; int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32; int gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32; int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; // Calculate the contribution from the five corners f64 t0 = 0.6 - x0*x0 - y0*y0 - z0*z0 - w0*w0; if (t0<0) n0 = 0.0; else { t0 *= t0; n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); } f64 t1 = 0.6 - x1*x1 - y1*y1 - z1*z1 - w1*w1; if (t1<0) n1 = 0.0; else { t1 *= t1; n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); } f64 t2 = 0.6 - x2*x2 - y2*y2 - z2*z2 - w2*w2; if (t2<0) n2 = 0.0; else { t2 *= t2; n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); } f64 t3 = 0.6 - x3*x3 - y3*y3 - z3*z3 - w3*w3; if (t3<0) n3 = 0.0; else { t3 *= t3; n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); } f64 t4 = 0.6 - x4*x4 - y4*y4 - z4*z4 - w4*w4; if (t4<0) n4 = 0.0; else { t4 *= t4; n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); } // Sum up and scale the result to cover the range [-1,1] return 27.0 * (n0 + n1 + n2 + n3 + n4); } ================================================ FILE: SoA/Noise.h ================================================ /// /// Noise.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// /// #pragma once #ifndef Noise_h__ #define Noise_h__ #include enum class TerrainStage { NOISE, SQUARED, CUBED, RIDGED_NOISE, ABS_NOISE, SQUARED_NOISE, CUBED_NOISE, CELLULAR_NOISE, CELLULAR_SQUARED_NOISE, CELLULAR_CUBED_NOISE, CONSTANT, PASS_THROUGH }; KEG_ENUM_DECL(TerrainStage); enum class TerrainOp { ADD = 0, SUB, MUL, DIV }; KEG_ENUM_DECL(TerrainOp); struct TerrainFuncProperties { TerrainStage func = TerrainStage::NOISE; TerrainOp op = TerrainOp::ADD; int octaves = 1; f64 persistence = 1.0; f64 frequency = 1.0; f64 low = -1.0; f64 high = 1.0; f64v2 clamp = f64v2(0.0); Array children; }; KEG_TYPE_DECL(TerrainFuncProperties); struct NoiseBase { f64 base = 0.0f; Array funcs; }; KEG_TYPE_DECL(NoiseBase); namespace Noise { f64v2 cellular(const f64v3& P); // Mulit-octave simplex noise f64 fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y); f64 fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y, const f64 z); f64 fractal(const int octaves, const f64 persistence, const f64 freq, const f64 x, const f64 y, const f64 z, const f64 w); // Raw Simplex noise - a single noise value. f64 raw(const f64 x, const f64 y); f64 raw(const f64 x, const f64 y, const f64 z); f64 raw(const f64 x, const f64 y, const f64, const f64 w); // Scaled Multi-octave Simplex noise // The result will be between the two parameters passed. inline f64 scaledFractal(const int octaves, const f64 persistence, const f64 freq, const f64 loBound, const f64 hiBound, const f64 x, const f64 y) { return fractal(octaves, persistence, freq, x, y) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } inline f64 scaledFractal(const int octaves, const f64 persistence, const f64 freq, const f64 loBound, const f64 hiBound, const f64 x, const f64 y, const f64 z) { return fractal(octaves, persistence, freq, x, y, z) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } inline f64 scaledFractal(const int octaves, const f64 persistence, const f64 freq, const f64 loBound, const f64 hiBound, const f64 x, const f64 y, const f64 z, const f64 w) { return fractal(octaves, persistence, freq, x, y, z, w) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } // Scaled Raw Simplex noise // The result will be between the two parameters passed. inline f64 scaledRaw(const f64 loBound, const f64 hiBound, const f64 x, const f64 y) { return raw(x, y) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } inline f64 scaledRaw(const f64 loBound, const f64 hiBound, const f64 x, const f64 y, const f64 z) { return raw(x, y, z) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } inline f64 scaledRaw(const f64 loBound, const f64 hiBound, const f64 x, const f64 y, const f64 z, const f64 w) { return raw(x, y, z, w) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2; } // The gradients are the midpoints of the vertices of a cube. const f64 grad3[12][3] = { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 }, { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 } }; // The gradients are the midpoints of the vertices of a hypercube. const f64 grad4[32][4] = { { 0, 1, 1, 1 }, { 0, 1, 1, -1 }, { 0, 1, -1, 1 }, { 0, 1, -1, -1 }, { 0, -1, 1, 1 }, { 0, -1, 1, -1 }, { 0, -1, -1, 1 }, { 0, -1, -1, -1 }, { 1, 0, 1, 1 }, { 1, 0, 1, -1 }, { 1, 0, -1, 1 }, { 1, 0, -1, -1 }, { -1, 0, 1, 1 }, { -1, 0, 1, -1 }, { -1, 0, -1, 1 }, { -1, 0, -1, -1 }, { 1, 1, 0, 1 }, { 1, 1, 0, -1 }, { 1, -1, 0, 1 }, { 1, -1, 0, -1 }, { -1, 1, 0, 1 }, { -1, 1, 0, -1 }, { -1, -1, 0, 1 }, { -1, -1, 0, -1 }, { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 }, { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, { -1, -1, 1, 0 }, { -1, -1, -1, 0 } }; // Permutation table. The same list is repeated twice. const int perm[512] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; // A lookup table to traverse the simplex around a given point in 4D. const int simplex[64][4] = { { 0, 1, 2, 3 }, { 0, 1, 3, 2 }, { 0, 0, 0, 0 }, { 0, 2, 3, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 3, 0 }, { 0, 2, 1, 3 }, { 0, 0, 0, 0 }, { 0, 3, 1, 2 }, { 0, 3, 2, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 3, 2, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 0, 3 }, { 0, 0, 0, 0 }, { 1, 3, 0, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 3, 0, 1 }, { 2, 3, 1, 0 }, { 1, 0, 2, 3 }, { 1, 0, 3, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 3, 1 }, { 0, 0, 0, 0 }, { 2, 1, 3, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 1, 3 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 0, 1, 2 }, { 3, 0, 2, 1 }, { 0, 0, 0, 0 }, { 3, 1, 2, 0 }, { 2, 1, 0, 3 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 1, 0, 2 }, { 0, 0, 0, 0 }, { 3, 2, 0, 1 }, { 3, 2, 1, 0 } }; } #endif // Noise_h__ ================================================ FILE: SoA/Octree.cpp ================================================ /* Implementations of Octree member functions. Copyright (C) 2011 Tao Ju This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stdafx.h" #include "Octree.h" #include "Density.h" // ---------------------------------------------------------------------------- const int MATERIAL_AIR = 0; const int MATERIAL_SOLID = 1; const float QEF_ERROR = 1e-6f; const int QEF_SWEEPS = 4; // ---------------------------------------------------------------------------- const i32v3 CHILD_MIN_OFFSETS[] = { // needs to match the vertMap from Dual Contouring impl i32v3(0, 0, 0), i32v3(0, 0, 1), i32v3(0, 1, 0), i32v3(0, 1, 1), i32v3(1, 0, 0), i32v3(1, 0, 1), i32v3(1, 1, 0), i32v3(1, 1, 1), }; // ---------------------------------------------------------------------------- // data from the original DC impl, drives the contouring process const int edgevmap[12][2] = { { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 }, // x-axis { 0, 2 }, { 1, 3 }, { 4, 6 }, { 5, 7 }, // y-axis { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } // z-axis }; // TODO: These weren't being used, why? // const int edgemask[3] = { 5, 3, 6 }; // const int vertMap[8][3] = // { // { 0, 0, 0 }, // { 0, 0, 1 }, // { 0, 1, 0 }, // { 0, 1, 1 }, // { 1, 0, 0 }, // { 1, 0, 1 }, // { 1, 1, 0 }, // { 1, 1, 1 } // }; // const int faceMap[6][4] = { { 4, 8, 5, 9 }, { 6, 10, 7, 11 }, { 0, 8, 1, 10 }, { 2, 9, 3, 11 }, { 0, 4, 2, 6 }, { 1, 5, 3, 7 } }; const int cellProcFaceMask[12][3] = { { 0, 4, 0 }, { 1, 5, 0 }, { 2, 6, 0 }, { 3, 7, 0 }, { 0, 2, 1 }, { 4, 6, 1 }, { 1, 3, 1 }, { 5, 7, 1 }, { 0, 1, 2 }, { 2, 3, 2 }, { 4, 5, 2 }, { 6, 7, 2 } }; const int cellProcEdgeMask[6][5] = { { 0, 1, 2, 3, 0 }, { 4, 5, 6, 7, 0 }, { 0, 4, 1, 5, 1 }, { 2, 6, 3, 7, 1 }, { 0, 2, 4, 6, 2 }, { 1, 3, 5, 7, 2 } }; const int faceProcFaceMask[3][4][3] = { { { 4, 0, 0 }, { 5, 1, 0 }, { 6, 2, 0 }, { 7, 3, 0 } }, { { 2, 0, 1 }, { 6, 4, 1 }, { 3, 1, 1 }, { 7, 5, 1 } }, { { 1, 0, 2 }, { 3, 2, 2 }, { 5, 4, 2 }, { 7, 6, 2 } } }; const int faceProcEdgeMask[3][4][6] = { { { 1, 4, 0, 5, 1, 1 }, { 1, 6, 2, 7, 3, 1 }, { 0, 4, 6, 0, 2, 2 }, { 0, 5, 7, 1, 3, 2 } }, { { 0, 2, 3, 0, 1, 0 }, { 0, 6, 7, 4, 5, 0 }, { 1, 2, 0, 6, 4, 2 }, { 1, 3, 1, 7, 5, 2 } }, { { 1, 1, 0, 3, 2, 0 }, { 1, 5, 4, 7, 6, 0 }, { 0, 1, 5, 0, 4, 1 }, { 0, 3, 7, 2, 6, 1 } } }; const int edgeProcEdgeMask[3][2][5] = { { { 3, 2, 1, 0, 0 }, { 7, 6, 5, 4, 0 } }, { { 5, 1, 4, 0, 1 }, { 7, 3, 6, 2, 1 } }, { { 6, 4, 2, 0, 2 }, { 7, 5, 3, 1, 2 } }, }; const int processEdgeMask[3][4] = { { 3, 2, 1, 0 }, { 7, 5, 6, 4 }, { 11, 10, 9, 8 } }; // ------------------------------------------------------------------------------- OctreeNode* SimplifyOctree(OctreeNode* node, float threshold) { if (!node) { return NULL; } if (node->type != Node_Internal) { // can't simplify! return node; } svd::QefSolver qef; int signs[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; int midsign = -1; int edgeCount = 0; bool isCollapsible = true; for (int i = 0; i < 8; i++) { node->children[i] = SimplifyOctree(node->children[i], threshold); if (node->children[i]) { OctreeNode* child = node->children[i]; if (child->type == Node_Internal) { isCollapsible = false; } else { qef.add(child->drawInfo->qef); midsign = (child->drawInfo->corners >> (7 - i)) & 1; signs[i] = (child->drawInfo->corners >> i) & 1; edgeCount++; } } } if (!isCollapsible) { // at least one child is an internal node, can't collapse return node; } svd::Vec3 qefPosition; qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); float error = qef.getError(); // convert to glm vec3 for ease of use f32v3 position(qefPosition.x, qefPosition.y, qefPosition.z); // at this point the masspoint will actually be a sum, so divide to make it the average if (error > threshold) { // this collapse breaches the threshold return node; } if (position.x < node->min.x || position.x >(node->min.x + node->size) || position.y < node->min.y || position.y >(node->min.y + node->size) || position.z < node->min.z || position.z >(node->min.z + node->size)) { const auto& mp = qef.getMassPoint(); position = f32v3(mp.x, mp.y, mp.z); } // change the node from an internal node to a 'psuedo leaf' node OctreeDrawInfo* drawInfo = new OctreeDrawInfo; for (int i = 0; i < 8; i++) { if (signs[i] == -1) { // Undetermined, use centre sign instead drawInfo->corners |= (midsign << i); } else { drawInfo->corners |= (signs[i] << i); } } drawInfo->averageNormal = f32v3(0.f); for (int i = 0; i < 8; i++) { if (node->children[i]) { OctreeNode* child = node->children[i]; if (child->type == Node_Psuedo || child->type == Node_Leaf) { drawInfo->averageNormal += child->drawInfo->averageNormal; } } } drawInfo->averageNormal = glm::normalize(drawInfo->averageNormal); drawInfo->position = position; drawInfo->qef = qef.getData(); for (int i = 0; i < 8; i++) { DestroyOctree(node->children[i]); node->children[i] = nullptr; } node->type = Node_Psuedo; node->drawInfo = drawInfo; return node; } // ---------------------------------------------------------------------------- void GenerateVertexIndices(OctreeNode* node, std::vector& vertexBuffer) { if (!node) { return; } if (node->type != Node_Leaf) { for (int i = 0; i < 8; i++) { GenerateVertexIndices(node->children[i], vertexBuffer); } } if (node->type != Node_Internal) { OctreeDrawInfo* d = node->drawInfo; if (!d) { printf("Error! Could not add vertex!\n"); exit(EXIT_FAILURE); } d->index = vertexBuffer.size(); vertexBuffer.push_back(VoxelModelVertex(d->position, color3(255, 255, 255), d->averageNormal)); } } // ---------------------------------------------------------------------------- void ContourProcessEdge(OctreeNode* node[4], int dir, std::vector& indexBuffer) { int minSize = 1000000; // arbitrary big number int minIndex = 0; int indices[4] = { -1, -1, -1, -1 }; bool flip = false; bool signChange[4] = { false, false, false, false }; for (int i = 0; i < 4; i++) { const int edge = processEdgeMask[dir][i]; const int c1 = edgevmap[edge][0]; const int c2 = edgevmap[edge][1]; const int m1 = (node[i]->drawInfo->corners >> c1) & 1; const int m2 = (node[i]->drawInfo->corners >> c2) & 1; if (node[i]->size < minSize) { minSize = node[i]->size; minIndex = i; flip = m1 != MATERIAL_AIR; } indices[i] = node[i]->drawInfo->index; signChange[i] = (m1 == MATERIAL_AIR && m2 != MATERIAL_AIR) || (m1 != MATERIAL_AIR && m2 == MATERIAL_AIR); } if (signChange[minIndex]) { if (!flip) { indexBuffer.push_back(indices[0]); indexBuffer.push_back(indices[1]); indexBuffer.push_back(indices[3]); indexBuffer.push_back(indices[0]); indexBuffer.push_back(indices[3]); indexBuffer.push_back(indices[2]); } else { indexBuffer.push_back(indices[0]); indexBuffer.push_back(indices[3]); indexBuffer.push_back(indices[1]); indexBuffer.push_back(indices[0]); indexBuffer.push_back(indices[2]); indexBuffer.push_back(indices[3]); } } } // ---------------------------------------------------------------------------- void ContourEdgeProc(OctreeNode* node[4], int dir, std::vector& indexBuffer) { if (!node[0] || !node[1] || !node[2] || !node[3]) { return; } if (node[0]->type != Node_Internal && node[1]->type != Node_Internal && node[2]->type != Node_Internal && node[3]->type != Node_Internal) { ContourProcessEdge(node, dir, indexBuffer); } else { for (int i = 0; i < 2; i++) { OctreeNode* edgeNodes[4]; const int c[4] = { edgeProcEdgeMask[dir][i][0], edgeProcEdgeMask[dir][i][1], edgeProcEdgeMask[dir][i][2], edgeProcEdgeMask[dir][i][3], }; for (int j = 0; j < 4; j++) { if (node[j]->type == Node_Leaf || node[j]->type == Node_Psuedo) { edgeNodes[j] = node[j]; } else { edgeNodes[j] = node[j]->children[c[j]]; } } ContourEdgeProc(edgeNodes, edgeProcEdgeMask[dir][i][4], indexBuffer); } } } // ---------------------------------------------------------------------------- void ContourFaceProc(OctreeNode* node[2], int dir, std::vector& indexBuffer) { if (!node[0] || !node[1]) { return; } if (node[0]->type == Node_Internal || node[1]->type == Node_Internal) { for (int i = 0; i < 4; i++) { OctreeNode* faceNodes[2]; const int c[2] = { faceProcFaceMask[dir][i][0], faceProcFaceMask[dir][i][1], }; for (int j = 0; j < 2; j++) { if (node[j]->type != Node_Internal) { faceNodes[j] = node[j]; } else { faceNodes[j] = node[j]->children[c[j]]; } } ContourFaceProc(faceNodes, faceProcFaceMask[dir][i][2], indexBuffer); } const int orders[2][4] = { { 0, 0, 1, 1 }, { 0, 1, 0, 1 }, }; for (int i = 0; i < 4; i++) { OctreeNode* edgeNodes[4]; const int c[4] = { faceProcEdgeMask[dir][i][1], faceProcEdgeMask[dir][i][2], faceProcEdgeMask[dir][i][3], faceProcEdgeMask[dir][i][4], }; const int* order = orders[faceProcEdgeMask[dir][i][0]]; for (int j = 0; j < 4; j++) { if (node[order[j]]->type == Node_Leaf || node[order[j]]->type == Node_Psuedo) { edgeNodes[j] = node[order[j]]; } else { edgeNodes[j] = node[order[j]]->children[c[j]]; } } ContourEdgeProc(edgeNodes, faceProcEdgeMask[dir][i][5], indexBuffer); } } } // ---------------------------------------------------------------------------- void ContourCellProc(OctreeNode* node, std::vector& indexBuffer) { if (node == NULL) { return; } if (node->type == Node_Internal) { for (int i = 0; i < 8; i++) { ContourCellProc(node->children[i], indexBuffer); } for (int i = 0; i < 12; i++) { OctreeNode* faceNodes[2]; const int c[2] = { cellProcFaceMask[i][0], cellProcFaceMask[i][1] }; faceNodes[0] = node->children[c[0]]; faceNodes[1] = node->children[c[1]]; ContourFaceProc(faceNodes, cellProcFaceMask[i][2], indexBuffer); } for (int i = 0; i < 6; i++) { OctreeNode* edgeNodes[4]; const int c[4] = { cellProcEdgeMask[i][0], cellProcEdgeMask[i][1], cellProcEdgeMask[i][2], cellProcEdgeMask[i][3], }; for (int j = 0; j < 4; j++) { edgeNodes[j] = node->children[c[j]]; } ContourEdgeProc(edgeNodes, cellProcEdgeMask[i][4], indexBuffer); } } } // ---------------------------------------------------------------------------- f32v3 ApproximateZeroCrossingPosition(const f32v3& p0, const f32v3& p1) { // approximate the zero crossing by finding the min value along the edge float minValue = 100000.f; float t = 0.f; float currentT = 0.f; const int steps = 8; const float increment = 1.f / (float)steps; while (currentT <= 1.f) { const f32v3 p = p0 + ((p1 - p0) * currentT); const float density = glm::abs(Density_Func(p)); if (density < minValue) { minValue = density; t = currentT; } currentT += increment; } return p0 + ((p1 - p0) * t); } // ---------------------------------------------------------------------------- f32v3 CalculateSurfaceNormal(const f32v3& p) { const float H = 0.001f; const float dx = Density_Func(p + f32v3(H, 0.f, 0.f)) - Density_Func(p - f32v3(H, 0.f, 0.f)); const float dy = Density_Func(p + f32v3(0.f, H, 0.f)) - Density_Func(p - f32v3(0.f, H, 0.f)); const float dz = Density_Func(p + f32v3(0.f, 0.f, H)) - Density_Func(p - f32v3(0.f, 0.f, H)); return glm::normalize(f32v3(dx, dy, dz)); } // ---------------------------------------------------------------------------- OctreeNode* ConstructLeaf(OctreeNode* leaf) { if (!leaf || leaf->size != 1) { return nullptr; } int corners = 0; for (int i = 0; i < 8; i++) { const i32v3 cornerPos = leaf->min + CHILD_MIN_OFFSETS[i]; const float density = Density_Func(f32v3(cornerPos)); const int material = density < 0.f ? MATERIAL_SOLID : MATERIAL_AIR; corners |= (material << i); } if (corners == 0 || corners == 255) { // voxel is full inside or outside the volume delete leaf; return nullptr; } // otherwise the voxel contains the surface, so find the edge intersections const int MAX_CROSSINGS = 6; int edgeCount = 0; f32v3 averageNormal(0.f); svd::QefSolver qef; for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) { const int c1 = edgevmap[i][0]; const int c2 = edgevmap[i][1]; const int m1 = (corners >> c1) & 1; const int m2 = (corners >> c2) & 1; if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID)) { // no zero crossing on this edge continue; } const f32v3 p1 = f32v3(leaf->min + CHILD_MIN_OFFSETS[c1]); const f32v3 p2 = f32v3(leaf->min + CHILD_MIN_OFFSETS[c2]); const f32v3 p = ApproximateZeroCrossingPosition(p1, p2); const f32v3 n = CalculateSurfaceNormal(p); qef.add(p.x, p.y, p.z, n.x, n.y, n.z); averageNormal += n; edgeCount++; } svd::Vec3 qefPosition; qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); OctreeDrawInfo* drawInfo = new OctreeDrawInfo; drawInfo->position = f32v3(qefPosition.x, qefPosition.y, qefPosition.z); drawInfo->qef = qef.getData(); const f32v3 min = f32v3(leaf->min); const f32v3 max = f32v3(leaf->min + i32v3(leaf->size)); if (drawInfo->position.x < min.x || drawInfo->position.x > max.x || drawInfo->position.y < min.y || drawInfo->position.y > max.y || drawInfo->position.z < min.z || drawInfo->position.z > max.z) { const auto& mp = qef.getMassPoint(); drawInfo->position = f32v3(mp.x, mp.y, mp.z); } drawInfo->averageNormal = glm::normalize(averageNormal / (float)edgeCount); drawInfo->corners = corners; leaf->type = Node_Leaf; leaf->drawInfo = drawInfo; return leaf; } // ------------------------------------------------------------------------------- OctreeNode* ConstructOctreeNodes(OctreeNode* node) { if (!node) { return nullptr; } if (node->size == 1) { return ConstructLeaf(node); } const int childSize = node->size / 2; bool hasChildren = false; for (int i = 0; i < 8; i++) { OctreeNode* child = new OctreeNode; child->size = childSize; child->min = node->min + (CHILD_MIN_OFFSETS[i] * childSize); child->type = Node_Internal; node->children[i] = ConstructOctreeNodes(child); hasChildren |= (node->children[i] != nullptr); } if (!hasChildren) { delete node; return nullptr; } return node; } // ------------------------------------------------------------------------------- OctreeNode* BuildOctree(const i32v3& min, const int size, const float threshold) { OctreeNode* root = new OctreeNode; root->min = min; root->size = size; root->type = Node_Internal; ConstructOctreeNodes(root); root = SimplifyOctree(root, threshold); return root; } // ---------------------------------------------------------------------------- void GenerateMeshFromOctree(OctreeNode* node, std::vector& vertexBuffer, std::vector& indexBuffer) { if (!node) { return; } vertexBuffer.clear(); indexBuffer.clear(); GenerateVertexIndices(node, vertexBuffer); ContourCellProc(node, indexBuffer); } // ------------------------------------------------------------------------------- void DestroyOctree(OctreeNode* node) { if (!node) { return; } for (int i = 0; i < 8; i++) { DestroyOctree(node->children[i]); } if (node->drawInfo) { delete node->drawInfo; } delete node; } ================================================ FILE: SoA/Octree.h ================================================ /* Implementations of Octree member functions. Copyright (C) 2011 Tao Ju This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef HAS_OCTREE_H_BEEN_INCLUDED #define HAS_OCTREE_H_BEEN_INCLUDED #include "qef.h" #include "VoxelModelMesh.h" // ---------------------------------------------------------------------------- enum OctreeNodeType { Node_None, Node_Internal, Node_Psuedo, Node_Leaf, }; // ---------------------------------------------------------------------------- struct OctreeDrawInfo { OctreeDrawInfo() : index(-1) , corners(0) { } int index; int corners; f32v3 position; f32v3 averageNormal; svd::QefData qef; }; // ---------------------------------------------------------------------------- class OctreeNode { public: OctreeNode() : type(Node_None) , min(0, 0, 0) , size(0) , drawInfo(nullptr) { memset(children, 0, sizeof(children)); } OctreeNode(const OctreeNodeType _type) : type(_type) , min(0, 0, 0) , size(0) , drawInfo(nullptr) { memset(children, 0, sizeof(children)); } OctreeNodeType type; i32v3 min; int size; OctreeNode* children[8]; OctreeDrawInfo* drawInfo; }; // ---------------------------------------------------------------------------- OctreeNode* BuildOctree(const i32v3& min, const int size, const float threshold); void DestroyOctree(OctreeNode* node); void GenerateMeshFromOctree(OctreeNode* node, std::vector& vertexBuffer, std::vector& indexBuffer); // ---------------------------------------------------------------------------- #endif // HAS_OCTREE_H_BEEN_INCLUDED ================================================ FILE: SoA/OpaqueVoxelRenderStage.cpp ================================================ #include "stdafx.h" #include "OpaqueVoxelRenderStage.h" #include #include #include "Camera.h" #include "Chunk.h" #include "BlockPack.h" #include "BlockTexturePack.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "SoaOptions.h" #include "RenderUtils.h" #include "soaUtils.h" #include "ShaderLoader.h" void OpaqueVoxelRenderStage::hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams) { m_gameRenderParams = gameRenderParams; m_renderer = renderer; } void OpaqueVoxelRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { ChunkMeshManager* cmm = m_gameRenderParams->chunkMeshmanager; const f64v3& position = m_gameRenderParams->chunkCamera->getPosition(); m_renderer->beginOpaque(m_gameRenderParams->blockTexturePack->getAtlasTexture(), m_gameRenderParams->sunlightDirection, m_gameRenderParams->sunlightColor); // f64v3 closestPoint; // static const f64v3 boxDims(CHUNK_WIDTH); static const f64v3 boxDims_2(CHUNK_WIDTH / 2); const std::vector & chunkMeshes = cmm->getChunkMeshes(); { std::lock_guard l(cmm->lckActiveChunkMeshes); if (chunkMeshes.empty()) return; for (int i = chunkMeshes.size() - 1; i >= 0; i--) { ChunkMesh* cm = chunkMeshes[i]; if (m_gameRenderParams->chunkCamera->sphereInFrustum(f32v3(cm->position + boxDims_2 - position), CHUNK_DIAGONAL_LENGTH)) { // TODO(Ben): Implement perfect fade cm->inFrustum = 1; m_renderer->drawOpaque(cm, position, m_gameRenderParams->chunkCamera->getViewProjectionMatrix()); } else { cm->inFrustum = 0; } } } m_renderer->end(); } ================================================ FILE: SoA/OpaqueVoxelRenderStage.h ================================================ /// /// OpaqueVoxelRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the render stage for opaque voxels. /// Opaque voxels have no transparency. /// #pragma once #ifndef OpaqueVoxelRenderStage_h__ #define OpaqueVoxelRenderStage_h__ #include "IRenderStage.h" #include class Camera; class ChunkRenderer; class GameRenderParams; class MeshManager; class OpaqueVoxelRenderStage : public IRenderStage { public: void hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams); /// Draws the render stage virtual void render(const Camera* camera) override; private: ChunkRenderer* m_renderer; const GameRenderParams* m_gameRenderParams; ///< Handle to some shared parameters }; #endif // OpaqueVoxelRenderStage_h__ ================================================ FILE: SoA/OptionsController.cpp ================================================ #include "stdafx.h" #include "OptionsController.h" #include OptionsController::OptionsController(const nString& filePath /*= "Data/options.ini"*/) : m_filePath(filePath) { // Empty } OptionsController::~OptionsController() { // Empty } void OptionsController::setDefault() { m_tempCopy = soaOptions; m_default = soaOptions; // TODO(Ben): This is wrong } void OptionsController::beginContext() { m_tempCopy = soaOptions; } // TODO(Ben): Better parsing bool OptionsController::loadOptions() { std::ifstream file(m_filePath); if (file.fail()) return false; nString token; nString dummy; while (std::getline(file, token, ':')) { int id = soaOptions.findID(token); if (id != -1) { SoaOption& opt = soaOptions.get(id); switch (opt.value.type) { case OptionValueType::F32: file >> opt.value.f; break; case OptionValueType::I32: file >> opt.value.i; break; case OptionValueType::BOOL: file >> opt.value.b; break; case OptionValueType::CHAR: file >> opt.value.c; break; default: file >> dummy; break; } char nl; file.get(nl); } } return true; } void OptionsController::saveOptions() { soaOptions = m_tempCopy; OptionsChange(); std::ofstream file(m_filePath); if (file.fail()) return; auto& options = soaOptions.getOptions(); for (int id = 0; id < (int)options.size(); id++) { auto& opt = options[id]; // Don't duplicated app.config if (id != OPT_SCREEN_HEIGHT && id != OPT_SCREEN_WIDTH && id != OPT_FULLSCREEN && id != OPT_BORDERLESS && id != OPT_VSYNC) { file << opt.name << ": "; switch (opt.value.type) { case OptionValueType::F32: file << opt.value.f << '\n'; break; case OptionValueType::I32: file << opt.value.i << '\n'; break; case OptionValueType::BOOL: file << opt.value.b << '\n'; break; case OptionValueType::CHAR: file << opt.value.c << '\n'; break; default: break; } } } } void OptionsController::restoreDefault() { soaOptions = m_default; m_tempCopy = m_default; OptionsChange(); } void OptionsController::setInt(nString optionName, int val) { auto& option = m_tempCopy.get(optionName); if (option.value.i != val) { option.value.i = val; } } void OptionsController::setFloat(nString optionName, f32 val) { auto& option = m_tempCopy.get(optionName); if (option.value.f != val) { option.value.f = val; } } void OptionsController::setBool(nString optionName, bool val) { auto& option = m_tempCopy.get(optionName); if (option.value.b != val) { option.value.b = val; } } int OptionsController::getInt(nString optionName) { return m_tempCopy.get(optionName).value.i; } f32 OptionsController::getFloat(nString optionName) { return m_tempCopy.get(optionName).value.f; } bool OptionsController::getBool(nString optionName) { return m_tempCopy.get(optionName).value.b; } ================================================ FILE: SoA/OptionsController.h ================================================ /// /// OptionsController.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 13 May 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles changing of options. /// #pragma once #ifndef OptionsController_h__ #define OptionsController_h__ #include "SoaOptions.h" #include #include class OptionsController { public: OptionsController(const nString& filePath = "Data/options.ini"); ~OptionsController(); // Call right before loading options void setDefault(); /// Begins a context for changing options. /// Call this when beginning to change options. void beginContext(); bool loadOptions(); void saveOptions(); void restoreDefault(); template void registerScripting(vscript::IEnvironment* env); // These can be called from lua scripts void setInt(nString optionName, int val); void setFloat(nString optionName, f32 val); void setBool(nString optionName, bool val); int getInt(nString optionName); f32 getFloat(nString optionName); bool getBool(nString optionName); bool needsFboReload = false; bool needsTextureReload = false; bool needsShaderReload = false; bool needsWindowReload = false; Event<> OptionsChange; private: nString m_filePath = ""; SoaOptions m_tempCopy; SoaOptions m_default; }; template void OptionsController::registerScripting(vscript::IEnvironment* env) { env->setNamespaces("Options"); env->addCDelegate("setInt", makeDelegate(this, &OptionsController::setInt)); env->addCDelegate("setFloat", makeDelegate(this, &OptionsController::setFloat)); env->addCDelegate("setBool", makeDelegate(this, &OptionsController::setBool)); env->addCDelegate("getInt", makeDelegate(this, &OptionsController::getInt)); env->addCDelegate("getFloat", makeDelegate(this, &OptionsController::getFloat)); env->addCDelegate("getBool", makeDelegate(this, &OptionsController::getBool)); env->addCDelegate("beginContext", makeDelegate(this, &OptionsController::beginContext)); env->addCDelegate("save", makeDelegate(this, &OptionsController::saveOptions)); env->addCDelegate("load", makeDelegate(this, &OptionsController::loadOptions)); env->addCDelegate("restoreDefault", makeDelegate(this, &OptionsController::restoreDefault)); env->setNamespaces(); } #endif // OptionsController_h__ ================================================ FILE: SoA/OrbitComponentRenderer.cpp ================================================ #include "stdafx.h" #include "OrbitComponentRenderer.h" #include #include #include #include "Constants.h" #include "RenderUtils.h" #include "SpaceSystemComponents.h" #include "OrbitComponentUpdater.h" void OrbitComponentRenderer::drawPath(OrbitComponent& cmp, vg::GLProgram& colorProgram, const f32m4& wvp, NamePositionComponent* npComponent VORB_MAYBE_UNUSED, const f64v3& camPos, float blendFactor, NamePositionComponent* parentNpComponent /*= nullptr*/) { // Lazily generate mesh if (cmp.vbo == 0) generateOrbitEllipse(cmp, colorProgram); if (cmp.numVerts == 0) return; f32m4 w(1.0f); if (parentNpComponent) { setMatrixTranslation(w, parentNpComponent->position - camPos); } else { setMatrixTranslation(w, -camPos); } f32m4 pathMatrix = wvp * w; f32v4 newColor = lerp(cmp.pathColor[0], cmp.pathColor[1], blendFactor); if (newColor.a <= 0.0f) return; glUniform4f(colorProgram.getUniform("unColor"), newColor.r, newColor.g, newColor.b, newColor.a); glUniformMatrix4fv(colorProgram.getUniform("unWVP"), 1, GL_FALSE, &pathMatrix[0][0]); float currentAngle = cmp.currentMeanAnomaly - (f32)cmp.startMeanAnomaly; glUniform1f(colorProgram.getUniform("currentAngle"), currentAngle / (float)M_2_PI); // Draw the ellipse glDepthMask(false); glBindVertexArray(cmp.vao); glDrawArrays(GL_LINE_STRIP, 0, cmp.numVerts); glBindVertexArray(0); glDepthMask(true); } void OrbitComponentRenderer::generateOrbitEllipse(OrbitComponent& cmp, vg::GLProgram& colorProgram) { if (cmp.verts.empty()) return; glGenVertexArrays(1, &cmp.vao); glBindVertexArray(cmp.vao); colorProgram.enableVertexAttribArrays(); // Upload the buffer data vg::GpuMemory::createBuffer(cmp.vbo); vg::GpuMemory::bindBuffer(cmp.vbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(cmp.vbo, vg::BufferTarget::ARRAY_BUFFER, cmp.verts.size() * sizeof(OrbitComponent::Vertex), cmp.verts.data(), vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(0, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(OrbitComponent::Vertex), 0); glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, sizeof(OrbitComponent::Vertex), (const void*)offsetof(OrbitComponent::Vertex, angle)); glBindVertexArray(0); cmp.numVerts = cmp.verts.size(); std::vector().swap(cmp.verts); } ================================================ FILE: SoA/OrbitComponentRenderer.h ================================================ /// /// OrbitComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders orbit components /// #pragma once #ifndef OrbitComponentRenderer_h__ #define OrbitComponentRenderer_h__ #include #include class SpaceSystem; struct OrbitComponent; struct NamePositionComponent; DECL_VG(class GLProgram) class OrbitComponentRenderer { public: /// Draws the ellipse void drawPath(OrbitComponent& cmp, vg::GLProgram& colorProgram, const f32m4& WVP, NamePositionComponent* npComponent, const f64v3& camPos, float blendFactor, NamePositionComponent* parentNpComponent = nullptr); private: void generateOrbitEllipse(OrbitComponent& cmp, vg::GLProgram& colorProgram); }; #endif // OrbitComponentRenderer_h__ ================================================ FILE: SoA/OrbitComponentUpdater.cpp ================================================ #include "stdafx.h" #include "OrbitComponentUpdater.h" #include "SpaceSystem.h" #include "Constants.h" #include "soaUtils.h" void OrbitComponentUpdater::update(SpaceSystem* spaceSystem, f64 time) { for (auto& it : spaceSystem->orbit) { auto& cmp = it.second; if (cmp.parentOrbId) { OrbitComponent* pOrbC = &spaceSystem->orbit.get(cmp.parentOrbId); updatePosition(cmp, time, &spaceSystem->namePosition.get(cmp.npID), pOrbC, &spaceSystem->namePosition.get(pOrbC->npID)); } else { updatePosition(cmp, time, &spaceSystem->namePosition.get(cmp.npID)); } } } void OrbitComponentUpdater::updatePosition(OrbitComponent& cmp, f64 time, NamePositionComponent* npComponent, OrbitComponent* parentOrbComponent /* = nullptr */, NamePositionComponent* parentNpComponent /* = nullptr */) { if (cmp.a == 0.0) return; /// Calculates position as a function of time /// http://en.wikipedia.org/wiki/Kepler%27s_laws_of_planetary_motion#Position_as_a_function_of_time // 1. Calculate the mean anomaly f64 meanAnomaly = (M_2_PI / cmp.t) * time + cmp.startMeanAnomaly; cmp.currentMeanAnomaly = (f32)meanAnomaly; f64 v = calculateTrueAnomaly(meanAnomaly, cmp.e); // Calculate radius // http://www.stargazing.net/kepler/ellipse.html f64 r = cmp.a * (1.0 - cmp.e * cmp.e) / (1.0 + cmp.e * cos(v)); f64 w = cmp.p - cmp.o; ///< Argument of periapsis // Calculate position f64v3 position; f64 cosv = cos(v + cmp.p - cmp.o); f64 sinv = sin(v + cmp.p - cmp.o); f64 coso = cos(cmp.o); f64 sino = sin(cmp.o); f64 cosi = cos(cmp.i); f64 sini = sin(cmp.i); position.x = r * (coso * cosv - sino * sinv * cosi); position.y = r * (sinv * sini); position.z = r * (sino * cosv + coso * sinv * cosi); // Calculate velocity f64 g = sqrt(M_G * KM_PER_M * cmp.parentMass * (2.0 / r - 1.0 / cmp.a)) * KM_PER_M; f64 sinwv = sin(w + v); cmp.relativeVelocity.x = -g * sinwv * cosi; cmp.relativeVelocity.y = g * sinwv * sini; cmp.relativeVelocity.z = g * cos(w + v); // If this planet has a parent, make it parent relative if (parentOrbComponent) { cmp.velocity = parentOrbComponent->velocity + cmp.relativeVelocity; npComponent->position = position + parentNpComponent->position; } else { cmp.velocity = cmp.relativeVelocity; npComponent->position = position; } } f64 OrbitComponentUpdater::calculateTrueAnomaly(f64 meanAnomaly, f64 e) { // 2. Solve Kepler's equation to compute eccentric anomaly // using Newton's method // http://www.jgiesen.de/kepler/kepler.html #define ITERATIONS 3 f64 E; ///< Eccentric Anomaly f64 F; E = meanAnomaly; for (int n = 0; n < ITERATIONS; n++) { F = E - e * sin(E) - meanAnomaly; E -= F / (1.0 - e * cos(E)); } // 3. Calculate true anomaly return atan2(sqrt(1.0 - e * e) * sin(E), cos(E) - e); } ================================================ FILE: SoA/OrbitComponentUpdater.h ================================================ /// /// OrbitComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates OrbitComponents /// #pragma once #ifndef OrbitComponentUpdater_h__ #define OrbitComponentUpdater_h__ #include class SpaceSystem; struct NamePositionComponent; struct OrbitComponent; struct SphericalGravityComponent; class OrbitComponentUpdater { public: void update(SpaceSystem* spaceSystem, f64 time); /// Updates the position based on time and parent position /// @param cmp: The component to update /// @param time: Time in seconds /// @param npComponent: The positional component of this component /// @param parentNpComponent: The parents positional component void updatePosition(OrbitComponent& cmp, f64 time, NamePositionComponent* npComponent, OrbitComponent* parentOrbComponent = nullptr, NamePositionComponent* parentNpComponent = nullptr); f64 calculateTrueAnomaly(f64 meanAnomaly, f64 e); }; #endif // OrbitComponentUpdater_h__ ================================================ FILE: SoA/PDA.cpp ================================================ #include "stdafx.h" #include "PDA.h" #include #include "GamePlayScreen.h" #include "ShaderLoader.h" PDA::PDA() { // Empty } PDA::~PDA() { // Empty } void PDA::init(GameplayScreen* ownerScreen VORB_UNUSED) { // Initialize the user interface } void PDA::open() { _isOpen = true; } void PDA::close() { _isOpen = false; } void PDA::update() { } void PDA::draw() const { if (!m_program) ShaderLoader::createProgramFromFile("Shaders/TextureShading/Texture2dShader.vert", "Shaders/TextureShading/Texture2dShader.frag"); } void PDA::destroy() { } ================================================ FILE: SoA/PDA.h ================================================ // // PDA.h // Seed Of Andromeda // // Created by Ben Arnold on 26 Oct 2014 // Copyright 2014 Regrowth Studios // MIT License // // This file provides the implementation for the // PDA. // #pragma once #ifndef PDA_H_ #define PDA_H_ #include #include #include "Computer.h" class GameplayScreen; enum class PdaState { BIOMETRICS, INVENTORY, DATA, CODEX, COMMUNICATIONS, SCANNER }; DECL_VG(class GLProgram) class PDA : public Computer { public: PDA(); ~PDA(); /// Initializes the PDA /// @param ownerScreen: The screen that owns this PDA void init(GameplayScreen* ownerScreen); /// Opens the PDA void open(); /// Closes the PDA void close(); /// Updates the PDA void update(); /// Draws the PDA void draw() const; /// Handles an event /// @param e: The event to handle void onEvent(const SDL_Event& e); /// Frees all resources void destroy(); /// Returns true if it is open bool isOpen() const { return _isOpen; } private: vg::GLProgram* m_program = nullptr; bool _isOpen = false; }; #endif // PDA_H_ ================================================ FILE: SoA/ParkourComponentUpdater.cpp ================================================ #include "stdafx.h" #include "ParkourComponentUpdater.h" #define GLM_FORCE_RADIANS #include "GameSystem.h" #include "SpaceSystem.h" #include "VoxelUtils.h" #include "SoAState.h" void ParkourComponentUpdater::update(GameSystem* gameSystem, SpaceSystem* spaceSystem VORB_UNUSED, const SoaState *soaState) { for (auto& it : gameSystem->parkourInput) { auto& parkour = it.second; auto& physics = gameSystem->physics.get(parkour.physicsComponent); if (physics.voxelPosition == 0) return; auto& attributes = gameSystem->attributes.get(parkour.attributeComponent); auto& voxelPosition = gameSystem->voxelPosition.get(physics.voxelPosition); auto& head = gameSystem->head.get(parkour.headComponent); auto& aabbCollidable = gameSystem->aabbCollidable.get(parkour.aabbCollidable); f64 deltaTime; if(m_lastTime<0.0f) deltaTime=0.0f; else deltaTime=soaState->time-m_lastTime; m_lastTime=soaState->time; // TODO(Ben): Timestep // TODO(Ben): Account mass // f64 acceleration = 1.0 + attributes.agility * 0.01; f64 maxSpeed = 0.5 + attributes.agility * 0.005; if (parkour.crouch) { maxSpeed *= 0.3f; } else if (parkour.sprint) { maxSpeed *= 2.0f; } f64v3 targetVel(0.0); int inputCount = 0; // Get move direction // TODO(Ben): Gamepad support if (parkour.moveForward) { targetVel.z = 1.0f; inputCount++; } else if (parkour.moveBackward) { targetVel.z = -1.0f; inputCount++; } if (parkour.moveLeft) { targetVel.x = 1.0f; inputCount++; } else if (parkour.moveRight) { targetVel.x = -1.0f; inputCount++; } // Get angles f64v3& euler = voxelPosition.eulerAngles; if (inputCount != 0) { // Normalize for diagonal if (inputCount == 2) { targetVel = glm::normalize(targetVel); } // Use head yaw for body when moving euler.y += head.eulerAngles.y; head.eulerAngles.y = 0.0f; head.relativeOrientation = f64q(head.eulerAngles); } // Check for pitch (Should be no pitch) if (euler.x != 0.0) { euler.x *= 0.95; if (ABS(euler.x) < 0.01) { euler.x = 0.0; } } // Check for roll (Should be no roll) if (euler.z != 0.0) { euler.z *= 0.95; if (ABS(euler.z) < 0.01) { euler.z = 0.0; } } voxelPosition.orientation = f64q(euler); targetVel *= (maxSpeed*deltaTime); targetVel = voxelPosition.orientation * targetVel; static const f64 step = 0.1; f64v3 dVel = targetVel - f64v3(physics.velocity.x, 0.0f, physics.velocity.z); f64 l = glm::length(dVel); if (l < step) { physics.velocity.x = targetVel.x; physics.velocity.z = targetVel.z; } else { physics.velocity += dVel * step; } // Collision if (aabbCollidable.voxelCollisions.size() && voxelPosition.parentVoxel) { const f64v3 MIN_DISTANCE = f64v3(aabbCollidable.box) * 0.5 + 0.5; for (auto& it : aabbCollidable.voxelCollisions) { for (auto& cd : it.second) { f64v3 aabbPos = voxelPosition.gridPosition.pos + f64v3(aabbCollidable.offset); f64v3 vpos = f64v3(it.first.x, it.first.y, it.first.z) * (f64)CHUNK_WIDTH + f64v3(getPosFromBlockIndex(cd.index)) + 0.5; f64v3 dp = vpos - aabbPos; f64v3 adp(glm::abs(dp)); // std::cout << MIN_DISTANCE.y - adp.y << std::endl; // Check slow feet collision first if (dp.y < 0 && MIN_DISTANCE.y - adp.y < 0.55 && !cd.top) { voxelPosition.gridPosition.y += (MIN_DISTANCE.y - adp.y) * 0.01; if (physics.velocity.y < 0) physics.velocity.y = 0.0; continue; } if (adp.y > adp.z && adp.y > adp.x) { // Y collision if (dp.y < 0) { if (!cd.top) { voxelPosition.gridPosition.y += MIN_DISTANCE.y - adp.y; if (physics.velocity.y < 0) physics.velocity.y = 0.0; continue; } } else { if (!cd.bottom) { voxelPosition.gridPosition.y -= MIN_DISTANCE.y - adp.y; if (physics.velocity.y > 0) physics.velocity.y = 0.0; continue; } } } if (adp.z > adp.x) { // Z collision if (dp.z < 0) { if (!cd.front) { voxelPosition.gridPosition.z += MIN_DISTANCE.z - adp.z; if (physics.velocity.z < 0) physics.velocity.z = 0.0; continue; } } else { if (!cd.back) { voxelPosition.gridPosition.z -= MIN_DISTANCE.z - adp.z; if (physics.velocity.z > 0) physics.velocity.z = 0.0; continue; } } } // X collision if (dp.x < 0) { if (!cd.right) { voxelPosition.gridPosition.x += MIN_DISTANCE.x - adp.x; if (physics.velocity.x < 0) physics.velocity.x = 0.0; } } else { if (!cd.left) { voxelPosition.gridPosition.x -= MIN_DISTANCE.x - adp.x; if (physics.velocity.x > 0) physics.velocity.x = 0.0; } } } } } } } ================================================ FILE: SoA/ParkourComponentUpdater.h ================================================ /// /// ParkourComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates ParkourComponents /// #pragma once #include #ifndef ParkourComponentUpdater_h__ #define ParkourComponentUpdater_h__ class GameSystem; class SpaceSystem; struct SoaState; class ParkourComponentUpdater { public: ParkourComponentUpdater():m_lastTime(-1.0f) {} void update(GameSystem* gameSystem, SpaceSystem* spaceSystem, const SoaState *soaState); f64 m_lastTime; }; #endif // ParkourComponentUpdater_h__ ================================================ FILE: SoA/ParticleMesh.h ================================================ #pragma once #include "WorldStructs.h" static const GLubyte particleUVs[12] = {255, 255, 0, 255, 0, 0, 0, 0, 255, 0, 255, 255}; class ParticleMesh { public: ParticleMesh(): uvBufferID(0), billboardVertexBufferID(0), vecIndex(-1), size(0), animated(0) {} GLuint uvBufferID, billboardVertexBufferID; int vecIndex, size, X, Y, Z, type; std::vector usedParticles; bool animated; }; class ParticleMeshMessage { public: ParticleMeshMessage(): mesh(NULL) {} ParticleMesh *mesh; std::vector verts; std::vector usedParticles; int size, X, Y, Z; }; ================================================ FILE: SoA/PauseMenu.cpp ================================================ #include "stdafx.h" #include "PauseMenu.h" #include "GamePlayScreen.h" #include "ShaderLoader.h" PauseMenu::PauseMenu() { // Empty } PauseMenu::~PauseMenu() { // Empty } void PauseMenu::init(GameplayScreen* ownerScreen VORB_UNUSED) { } void PauseMenu::open() { _isOpen = true; } void PauseMenu::close() { if (_isOpen) { _isOpen = false; } } void PauseMenu::update() { } void PauseMenu::draw() const { if (!m_program) ShaderLoader::createProgramFromFile("Shaders/TextureShading/Texture2dShader.vert", "Shaders/TextureShading/Texture2dShader.frag"); } void PauseMenu::destroy() { } ================================================ FILE: SoA/PauseMenu.h ================================================ /// /// PauseMenu.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 30 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Pause menu implementation for use in GamePlayScreen /// #pragma once #ifndef PauseMenu_h__ #define PauseMenu_h__ #include DECL_VG(class GLProgram); class GameplayScreen; class PauseMenu { public: PauseMenu(); ~PauseMenu(); /// Initializes the Pause Menu /// @param ownerScreen: The screen that owns this pause menu void init(GameplayScreen* ownerScreen); /// Opens the Pause Menu void open(); /// Closes the Pause Menu void close(); /// Updates the Pause Menu void update(); /// Draws the Pause Menu void draw() const; /// Handles an event /// @param e: The event to handle //void onEvent(const SDL_Event& e); /// Frees all resources void destroy(); /// Returns true if the Pause Menu is open const bool& isOpen() const { return _isOpen; } private: vg::GLProgram* m_program = nullptr; bool _isOpen = false; }; #endif // PauseMenu_h__ ================================================ FILE: SoA/PauseMenuRenderStage.cpp ================================================ #include "stdafx.h" #include "PauseMenuRenderStage.h" #include "PauseMenu.h" void PauseMenuRenderStage::hook(const PauseMenu* pauseMenu) { _pauseMenu = pauseMenu; } void PauseMenuRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED /*= nullptr*/) { if (_pauseMenu->isOpen()) { _pauseMenu->draw(); } } ================================================ FILE: SoA/PauseMenuRenderStage.h ================================================ /// /// PauseMenuRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 30 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Render stage for drawing the pause menu /// #pragma once #ifndef PauseMenuRenderStage_h__ #define PauseMenuRenderStage_h__ #include "IRenderStage.h" class PauseMenu; class PauseMenuRenderStage : public IRenderStage { public: void hook(const PauseMenu* pauseMenu); // Draws the render stage virtual void render(const Camera* camera = nullptr) override; private: const PauseMenu* _pauseMenu = nullptr; ///< Handle to pause menu for rendering }; #endif // PauseMenuRenderStage_h__ ================================================ FILE: SoA/PdaRenderStage.cpp ================================================ #include "stdafx.h" #include "PdaRenderStage.h" #include "PDA.h" PdaRenderStage::PdaRenderStage() { // Empty } void PdaRenderStage::hook(const PDA* pda) { _pda = pda; } void PdaRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { if (_pda->isOpen()) { _pda->draw(); } } ================================================ FILE: SoA/PdaRenderStage.h ================================================ /// /// PdaRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 2 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the render stage for PDA rendering /// #pragma once #ifndef PdaRenderStage_h__ #define PdaRenderStage_h__ #include "IRenderStage.h" class PDA; class PdaRenderStage : public IRenderStage { public: PdaRenderStage(); void hook(const PDA* pda); /// Draws the render stage virtual void render(const Camera* camera) override; private: const PDA* _pda = nullptr; ///< Handle to the PDA }; #endif // PdaRenderStage_h__ ================================================ FILE: SoA/PhysicsBlockRenderStage.cpp ================================================ #include "stdafx.h" #include "PhysicsBlockRenderStage.h" #include "Camera.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "SoaOptions.h" // TODO: Do we still want this as is? If so, reimplement and remove VORB_UNUSED tags. PhysicsBlockRenderStage::PhysicsBlockRenderStage(GameRenderParams* gameRenderParams VORB_UNUSED, const std::vector& physicsBlockMeshes VORB_UNUSED, vg::GLProgram* glProgram VORB_UNUSED) //: // _gameRenderParams(gameRenderParams), // _physicsBlockMeshes(physicsBlockMeshes), // _glProgram(glProgram) { // Empty } void PhysicsBlockRenderStage::render(const Camera* camera VORB_UNUSED) { /* _glProgram->use(); glUniform1f(_glProgram->getUniform("lightType"), _gameRenderParams->lightActive); glUniform1f(_glProgram->getUniform("alphaMult"), 1.0f); glUniform3fv(_glProgram->getUniform("eyeNormalWorldspace"), 1, &(_gameRenderParams->chunkCamera->getDirection()[0])); glUniform1f(_glProgram->getUniform("fogEnd"), _gameRenderParams->fogEnd); glUniform1f(_glProgram->getUniform("fogStart"), _gameRenderParams->fogStart); glUniform3fv(_glProgram->getUniform("fogColor"), 1, &(_gameRenderParams->fogColor[0])); glUniform3fv(_glProgram->getUniform("lightPosition_worldspace"), 1, &(_gameRenderParams->sunlightDirection[0])); glUniform1f(_glProgram->getUniform("specularExponent"), soaOptions.get(OPT_SPECULAR_EXPONENT).value.f); glUniform1f(_glProgram->getUniform("specularIntensity"), soaOptions.get(OPT_SPECULAR_INTENSITY).value.f * 0.3f); glUniform1f(_glProgram->getUniform("sunVal"), _gameRenderParams->sunlightIntensity); float blockAmbient = 0.000f; glUniform3f(_glProgram->getUniform("ambientLight"), blockAmbient, blockAmbient, blockAmbient); glUniform3fv(_glProgram->getUniform("lightColor"), 1, &(_gameRenderParams->sunlightColor[0])); glUniform1f(_glProgram->getUniform("fadeDistance"), (GLfloat)soaOptions.get(OPT_VOXEL_RENDER_DISTANCE).value.f - 12.5f); for (size_t i = 0; i < _physicsBlockMeshes.size(); i++) { PhysicsBlockBatch::draw(_physicsBlockMeshes[i], _glProgram, _gameRenderParams->chunkCamera->getPosition(), _gameRenderParams->chunkCamera->getViewProjectionMatrix()); } _glProgram->unuse();*/ } ================================================ FILE: SoA/PhysicsBlockRenderStage.h ================================================ /// /// PhysicsBlockRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 9 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// This file implements the physics block rendering, /// #pragma once #ifndef PhysicsBlockRenderStage_h__ #define PhysicsBlockRenderStage_h__ #include #include "IRenderStage.h" class GameRenderParams; class PhysicsBlockMesh; class PhysicsBlockRenderStage : public IRenderStage { public: /// Construcst the stage and injects dependencies /// @param gameRenderParams: Some shared parameters /// @param physicsBlockMeshes: Handle to the list of meshes to render /// @param glProgram: The program used to draw physics blocks PhysicsBlockRenderStage(GameRenderParams* gameRenderParams, const std::vector& physicsBlockMeshes, vg::GLProgram* glProgram); // Draws the render stage virtual void render(const Camera* camera) override; private: // vg::GLProgram* _glProgram; ///< Shader program that renders the voxels // GameRenderParams* _gameRenderParams; ///< Some shared voxel rendering params // const std::vector& _physicsBlockMeshes; ///< The meshes to render }; #endif // PhysicsBlockRenderStage_h__ ================================================ FILE: SoA/PhysicsComponentUpdater.cpp ================================================ #include "stdafx.h" #include "PhysicsComponentUpdater.h" #include "GameSystem.h" #include "GameSystemAssemblages.h" #include "SpaceSystem.h" #include "TerrainPatch.h" #include "VoxelSpaceConversions.h" #include "VoxelSpaceUtils.h" #include "soaUtils.h" #include // TODO(Ben): This is temporary for gravity #define FPS 60.0 // Exit is slightly bigger to prevent oscillation #define ENTRY_RADIUS_MULT 1.02 #define EXIT_RADIUS_MULT 1.0205 // TODO(Ben): Timestep void PhysicsComponentUpdater::update(GameSystem* gameSystem, SpaceSystem* spaceSystem) { for (auto& it : gameSystem->physics) { auto& cmp = it.second; // Voxel position dictates space position if (cmp.voxelPosition) { updateVoxelPhysics(gameSystem, spaceSystem, cmp, it.first); } else { updateSpacePhysics(gameSystem, spaceSystem, cmp, it.first); } } } f64v3 PhysicsComponentUpdater::calculateGravityAcceleration(f64v3 relativePosition, f64 mass) { f64 dist2 = glm::dot(relativePosition, relativePosition); // Get distance^2 relativePosition /= sqrt(dist2); // Normalize position f64 fgrav = M_G * mass / (dist2 * M_PER_KM * M_PER_KM); // Calculate force return relativePosition * ((fgrav / M_PER_KM) / FPS); // Return acceleration vector } // TODO(Ben): This is a clusterfuck void PhysicsComponentUpdater::updateVoxelPhysics(GameSystem* gameSystem, SpaceSystem* spaceSystem, PhysicsComponent& pyCmp, vecs::EntityID entity) { // Get the position component auto& spCmp = gameSystem->spacePosition.get(pyCmp.spacePosition); // Check for removal of spherical voxel component auto& stCmp = spaceSystem->sphericalTerrain.get(spCmp.parentSphericalTerrain); if (stCmp.sphericalVoxelComponent == 0) { // We need to transition to space pyCmp.voxelPosition = 0; // TODO(Ben): Orient this pyCmp.velocity = f64v3(0.0); GameSystemAssemblages::removeVoxelPosition(gameSystem, entity); GameSystemAssemblages::removeChunkSphere(gameSystem, entity); return; } auto& vpcmp = gameSystem->voxelPosition.get(pyCmp.voxelPosition); auto& svcmp = spaceSystem->sphericalVoxel.get(vpcmp.parentVoxel); // auto& npcmp = spaceSystem->namePosition.get(svcmp.namePositionComponent); auto& arcmp = spaceSystem->axisRotation.get(svcmp.axisRotationComponent); // Apply gravity if (spCmp.parentGravity) { auto& gravCmp = spaceSystem->sphericalGravity.get(spCmp.parentGravity); f64 height = (vpcmp.gridPosition.pos.y + svcmp.voxelRadius) * M_PER_VOXEL; f64 fgrav = M_G * gravCmp.mass / (height * height); ChunkID id(VoxelSpaceConversions::voxelToChunk(vpcmp.gridPosition)); ChunkHandle chunk = svcmp.chunkGrids[vpcmp.gridPosition.face].accessor.acquire(id); // Don't apply gravity in non generated chunks. if (chunk->genLevel == GEN_DONE) { pyCmp.velocity.y -= (fgrav * 0.1) / FPS; } chunk.release(); } // Update position vpcmp.gridPosition.pos += pyCmp.velocity; // Store old position in case of transition VoxelPositionComponent oldVPCmp = vpcmp; SpacePositionComponent oldSPCmp = spCmp; // Check transition to new face bool didTransition = false; if (vpcmp.gridPosition.pos.x < -svcmp.voxelRadius) { didTransition = true; transitionNegX(vpcmp, pyCmp, (f32)svcmp.voxelRadius); } else if (vpcmp.gridPosition.pos.x > svcmp.voxelRadius) { didTransition = true; transitionPosX(vpcmp, pyCmp, (f32)svcmp.voxelRadius); } else if (vpcmp.gridPosition.pos.z < -svcmp.voxelRadius) { didTransition = true; transitionNegZ(vpcmp, pyCmp, (f32)svcmp.voxelRadius); } else if (vpcmp.gridPosition.pos.z > svcmp.voxelRadius) { didTransition = true; transitionPosZ(vpcmp, pyCmp, (f32)svcmp.voxelRadius); } // Compute the relative space position and orientation from voxel position and orientation spCmp.position = arcmp.currentOrientation * VoxelSpaceConversions::voxelToWorld(vpcmp.gridPosition, svcmp.voxelRadius) * KM_PER_VOXEL; // TODO(Ben): This is expensive as fuck. Make sure you only do this for components that actually need it spCmp.orientation = arcmp.currentOrientation * VoxelSpaceUtils::calculateVoxelToSpaceQuat(vpcmp.gridPosition, svcmp.voxelRadius) * vpcmp.orientation; // Check transitions // TODO(Ben): This assumes a single player entity! if (spCmp.parentSphericalTerrain) { auto& stCmp = spaceSystem->sphericalTerrain.get(spCmp.parentSphericalTerrain); f64 distance = glm::length(spCmp.position); // Check transition to Space if (distance > stCmp.sphericalTerrainData->radius * EXIT_RADIUS_MULT) { if (stCmp.needsVoxelComponent) { stCmp.needsVoxelComponent = false; // Begin fade transition stCmp.alpha = TERRAIN_INC_START_ALPHA; } } else if (didTransition) { // Check face transition // Check face-to-face transition cases if (stCmp.transitionFace == FACE_NONE && !stCmp.isFaceTransitioning) { stCmp.transitionFace = vpcmp.gridPosition.face; stCmp.isFaceTransitioning = true; vpcmp = oldVPCmp; spCmp = oldSPCmp; } else if (stCmp.isFaceTransitioning) { // Check for transition end if (stCmp.faceTransTime < 0.2f) { stCmp.isFaceTransitioning = false; } else { vpcmp = oldVPCmp; spCmp = oldSPCmp; } } } } else { // This really shouldn't happen std::cerr << "Missing parent spherical terrain ID in updateVoxelPhysics\n"; } } void PhysicsComponentUpdater::updateSpacePhysics(GameSystem* gameSystem, SpaceSystem* spaceSystem, PhysicsComponent& pyCmp, vecs::EntityID entity) { // Get the position component auto& spCmp = gameSystem->spacePosition.get(pyCmp.spacePosition); // Apply gravity // TODO(Ben): Optimize and fix with timestep if (spCmp.parentGravity) { auto& gravCmp = spaceSystem->sphericalGravity.get(spCmp.parentGravity); pyCmp.velocity += calculateGravityAcceleration(-spCmp.position, gravCmp.mass); } else { // TODO(Ben): Check gravity on all planets? check transition to parent? } // Update position spCmp.position += pyCmp.velocity; // * timestep // Check transition to planet // TODO(Ben): This assumes a single player entity! if (spCmp.parentSphericalTerrain) { auto& stCmp = spaceSystem->sphericalTerrain.get(spCmp.parentSphericalTerrain); // auto& npCmp = spaceSystem->namePosition.get(stCmp.namePositionComponent); f64 distance = glm::length(spCmp.position); if (distance <= stCmp.sphericalTerrainData->radius * ENTRY_RADIUS_MULT) { // Mark the terrain component as needing voxels if (!stCmp.needsVoxelComponent) { auto& arCmp = spaceSystem->axisRotation.getFromEntity(stCmp.axisRotationComponent); stCmp.startVoxelPosition = VoxelSpaceConversions::worldToVoxel(arCmp.invCurrentOrientation * spCmp.position * VOXELS_PER_KM, stCmp.sphericalTerrainData->radius * VOXELS_PER_KM); stCmp.needsVoxelComponent = true; // Start the fade transition stCmp.alpha = TERRAIN_DEC_START_ALPHA; } else if (!pyCmp.voxelPosition && stCmp.sphericalVoxelComponent) { // Check if we need to create the voxelPosition component auto& arCmp = spaceSystem->axisRotation.getFromEntity(stCmp.axisRotationComponent); // Calculate voxel relative orientation f64q voxOrientation = glm::inverse(VoxelSpaceUtils::calculateVoxelToSpaceQuat(stCmp.startVoxelPosition, stCmp.sphericalTerrainData->radius * VOXELS_PER_KM)) * arCmp.invCurrentOrientation * spCmp.orientation; // Make the voxel position component vecs::ComponentID vpid = GameSystemAssemblages::addVoxelPosition(gameSystem, entity, stCmp.sphericalVoxelComponent, voxOrientation, stCmp.startVoxelPosition); // Make the Chunk Sphere component GameSystemAssemblages::addChunkSphere(gameSystem, entity, vpid, VoxelSpaceConversions::voxelToChunk(stCmp.startVoxelPosition), 7); auto& hcmp = gameSystem->head.getFromEntity(entity); hcmp.voxelPosition = vpid; pyCmp.voxelPosition = vpid; // TODO(Ben): Calculate velocity change properly //pyCmp.velocity = voxOrientation * pyCmp.velocity; pyCmp.velocity = f64v3(0); // VOXELS_PER_KM; // Update dependencies for frustum gameSystem->frustum.getFromEntity(entity).voxelPosition = vpid; } } } } #define VOXEL_PUSH CHUNK_WIDTH void PhysicsComponentUpdater::transitionPosX(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp VORB_MAYBE_UNUSED, float voxelRadius) { // Push in by a chunk float rad = voxelRadius - VOXEL_PUSH; // We could use lookup tables for this, but this is easier switch (vpCmp.gridPosition.face) { case FACE_TOP: vpCmp.orientation = f64q(f64v3(0.0, -M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_RIGHT; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.z; vpCmp.gridPosition.pos.z = -rad; break; case FACE_LEFT: vpCmp.gridPosition.face = FACE_FRONT; vpCmp.gridPosition.pos.x = -rad; break; case FACE_RIGHT: vpCmp.gridPosition.face = FACE_BACK; vpCmp.gridPosition.pos.x = -rad; break; case FACE_FRONT: vpCmp.gridPosition.face = FACE_RIGHT; vpCmp.gridPosition.pos.x = -rad; break; case FACE_BACK: vpCmp.gridPosition.face = FACE_LEFT; vpCmp.gridPosition.pos.x = -rad; break; case FACE_BOTTOM: vpCmp.orientation = f64q(f64v3(0.0, M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_RIGHT; vpCmp.gridPosition.pos.x = vpCmp.gridPosition.pos.z; vpCmp.gridPosition.pos.z = rad; break; default: std::cerr << "Invalid face in PhysicsComponentUpdater::transitionPosX\n"; break; } } void PhysicsComponentUpdater::transitionNegX(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp VORB_MAYBE_UNUSED, float voxelRadius) { // Push in by a chunk float rad = voxelRadius - VOXEL_PUSH; // We could use lookup tables for this, but this is easier switch (vpCmp.gridPosition.face) { case FACE_TOP: vpCmp.orientation = f64q(f64v3(0.0, M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_LEFT; vpCmp.gridPosition.pos.x = vpCmp.gridPosition.pos.z; vpCmp.gridPosition.pos.z = -rad; break; case FACE_LEFT: vpCmp.gridPosition.face = FACE_BACK; vpCmp.gridPosition.pos.x = rad; break; case FACE_RIGHT: vpCmp.gridPosition.face = FACE_FRONT; vpCmp.gridPosition.pos.x = rad; break; case FACE_FRONT: vpCmp.gridPosition.face = FACE_LEFT; vpCmp.gridPosition.pos.x = rad; break; case FACE_BACK: vpCmp.gridPosition.face = FACE_RIGHT; vpCmp.gridPosition.pos.x = rad; break; case FACE_BOTTOM: vpCmp.orientation = f64q(f64v3(0.0, -M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_LEFT; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.z; vpCmp.gridPosition.pos.z = rad; break; default: std::cerr << "Invalid face in PhysicsComponentUpdater::transitionPosX\n"; break; } } void PhysicsComponentUpdater::transitionPosZ(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp VORB_MAYBE_UNUSED, float voxelRadius) { // Push in by a chunk float rad = voxelRadius - VOXEL_PUSH; // We could use lookup tables for this, but this is easier switch (vpCmp.gridPosition.face) { case FACE_TOP: vpCmp.gridPosition.face = FACE_FRONT; vpCmp.gridPosition.pos.z = -rad; break; case FACE_LEFT: vpCmp.orientation = f64q(f64v3(0.0, M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_BOTTOM; vpCmp.gridPosition.pos.z = -vpCmp.gridPosition.pos.x; vpCmp.gridPosition.pos.x = -rad; break; case FACE_RIGHT: vpCmp.orientation = f64q(f64v3(0.0, -M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_BOTTOM; vpCmp.gridPosition.pos.z = vpCmp.gridPosition.pos.x; vpCmp.gridPosition.pos.x = rad; break; case FACE_FRONT: vpCmp.gridPosition.face = FACE_BOTTOM; vpCmp.gridPosition.pos.z = -rad; break; case FACE_BACK: vpCmp.orientation = f64q(f64v3(0.0, M_PI, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_BOTTOM; vpCmp.gridPosition.pos.z = -rad; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.x; break; case FACE_BOTTOM: vpCmp.orientation = f64q(f64v3(0.0, M_PI, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_BACK; vpCmp.gridPosition.pos.z = -rad; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.x; break; default: std::cerr << "Invalid face in PhysicsComponentUpdater::transitionPosX\n"; break; } } void PhysicsComponentUpdater::transitionNegZ(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp VORB_MAYBE_UNUSED, float voxelRadius) { // Push in by a chunk float rad = voxelRadius - VOXEL_PUSH; // We could use lookup tables for this, but this is easier switch (vpCmp.gridPosition.face) { case FACE_TOP: vpCmp.orientation = f64q(f64v3(0.0, M_PI, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_BACK; vpCmp.gridPosition.pos.z = -rad; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.x; break; case FACE_LEFT: vpCmp.orientation = f64q(f64v3(0.0, -M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_TOP; vpCmp.gridPosition.pos.z = vpCmp.gridPosition.pos.x; vpCmp.gridPosition.pos.x = -rad; break; case FACE_RIGHT: vpCmp.orientation = f64q(f64v3(0.0, M_PI / 2.0, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_TOP; vpCmp.gridPosition.pos.z = -vpCmp.gridPosition.pos.x; vpCmp.gridPosition.pos.x = rad; break; case FACE_FRONT: vpCmp.gridPosition.face = FACE_TOP; vpCmp.gridPosition.pos.z = rad; break; case FACE_BACK: vpCmp.orientation = f64q(f64v3(0.0, M_PI, 0.0)) * vpCmp.orientation; vpCmp.gridPosition.face = FACE_TOP; vpCmp.gridPosition.pos.z = -rad; vpCmp.gridPosition.pos.x = -vpCmp.gridPosition.pos.x; break; case FACE_BOTTOM: vpCmp.gridPosition.face = FACE_FRONT; vpCmp.gridPosition.pos.z = rad; break; default: std::cerr << "Invalid face in PhysicsComponentUpdater::transitionPosX\n"; break; } } #undef VOXEL_PUSH ================================================ FILE: SoA/PhysicsComponentUpdater.h ================================================ /// /// PhysicsComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates physics components /// #pragma once #ifndef PhysicsComponentUpdater_h__ #define PhysicsComponentUpdater_h__ class GameSystem; class SpaceSystem; struct PhysicsComponent; struct VoxelPositionComponent; #include // TODO(Ben): Timestep class PhysicsComponentUpdater { public: /// Updates physics components /// @param gameSystem: Game ECS /// @param spaceSystem: Space ECS. void update(GameSystem* gameSystem, SpaceSystem* spaceSystem); /// Calculates the acceleration vector due to gravity /// @relativePosition: Relative position of object to attractor /// @mass: Mass of the attractor /// @return the acceleration vector static f64v3 calculateGravityAcceleration(f64v3 relativePosition, f64 mass); private: void updateVoxelPhysics(GameSystem* gameSystem, SpaceSystem* spaceSystem, PhysicsComponent& pyCmp, vecs::EntityID entity); void updateSpacePhysics(GameSystem* gameSystem, SpaceSystem* spaceSystem, PhysicsComponent& pyCmp, vecs::EntityID entity); void transitionPosX(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp, float voxelRadius); void transitionNegX(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp, float voxelRadius); void transitionPosZ(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp, float voxelRadius); void transitionNegZ(VoxelPositionComponent& vpCmp, PhysicsComponent& pyCmp, float voxelRadius); }; #endif // PhysicsComponentUpdater_h__ ================================================ FILE: SoA/Planet.cpp ================================================ #include "stdafx.h" #include "Planet.h" #include #include #include #include #include #include "Camera.h" //#include "FileSystem.h" #include "GameManager.h" //#include "InputManager.h" #include "Inputs.h" //#include "ObjectLoader.h" //#include "Options.h" //#include "Rendering.h" //#include "TerrainGenerator.h" //#include "TerrainPatch.h" //#include "TexturePackLoader.h" //ObjectLoader objectLoader; Planet::Planet() { bindex = 0; stormNoiseFunction = NULL; sunnyCloudyNoiseFunction = NULL; cumulusNoiseFunction = NULL; rotationTheta = 0; rotationMatrix = glm::mat4(1.0); invRotationMatrix = glm::inverse(rotationMatrix); axialZTilt = 0.4; baseTemperature = 100; baseRainfall = 100; minHumidity = 0.0f; maxHumidity = 100.0f; minCelsius = -20.0f; maxCelsius = 60.0f; solarX = solarY = solarZ = 0; scaledRadius = 0; gravityConstant = 1.0; density = 1.0; } Planet::~Planet() { destroy(); } void Planet::clearMeshes() { // TerrainBuffers *tb; // for (int k = 0; k < 6; k++){ // for (size_t i = 0; i < drawList[k].size(); i++){ // tb = drawList[k][i]; // if (tb->vaoID != 0) glDeleteVertexArrays(1, &(tb->vaoID)); // if (tb->vboID != 0) glDeleteBuffers(1, &(tb->vboID)); // if (tb->treeVboID != 0) glDeleteBuffers(1, &(tb->treeVboID)); // if (tb->vboIndexID != 0) glDeleteBuffers(1, &(tb->vboIndexID)); // delete tb; // } // // drawList[k].clear(); // } // for (size_t i = 0; i < 6U; i++){ // for (size_t j = 0; j < faces[i].size(); j++){ // for (size_t k = 0; k < faces[i][j].size(); k++){ // faces[i][j][k]->NullifyBuffers(); // } // } // } } void Planet::initialize(nString filePath) { #define MAP_WIDTH 256 #define DEFAULT_RADIUS 1000000 if (filePath == "Worlds/(Empty Planet)/"){ radius = DEFAULT_RADIUS; scaledRadius = (radius - radius%CHUNK_WIDTH) / planetScale; int width = scaledRadius / TerrainPatchWidth * 2; if (width % 2 == 0){ // must be odd width++; } scaledRadius = (width*TerrainPatchWidth) / 2; radius = (int)(scaledRadius*planetScale); ColorRGB8* colorMap = GameManager::texturePackLoader->getColorMap("biome"); ColorRGB8* waterColorMap = GameManager::texturePackLoader->getColorMap("water"); for (int i = 0; i < MAP_WIDTH * MAP_WIDTH; i++){ colorMap[i] = ColorRGB8(0, 255, 0); } for (int i = 0; i < MAP_WIDTH * MAP_WIDTH; i++){ colorMap[i] = ColorRGB8(0, 0, 255); } for (int i = 0; i < 256; i++){ for (int j = 0; j < 256; j++){ int hexcode = 0; GameManager::terrainGenerator->BiomeMap[i][j] = hexcode; } } atmosphere.initialize("", scaledRadius); } else{ loadData(filePath, 0); atmosphere.initialize(filePath + "Sky/properties.ini", scaledRadius); } dirName = filePath; rotationTheta = 0; glm::vec3 EulerAngles(0, rotationTheta, axialZTilt); rotateQuaternion = glm::quat(EulerAngles); rotationMatrix = glm::toMat4(rotateQuaternion); double mrad = (double)radius / 2.0; volume = (4.0 / 3.0)*M_PI*mrad*mrad*mrad; mass = volume*density; facecsGridWidth = (radius * 2) / CHUNK_WIDTH; } void Planet::initializeTerrain(const glm::dvec3 &startPosition) { int lodx, lody, lodz; int wx = (int)startPosition.x, wy = (int)startPosition.y, wz = (int)startPosition.z; int width = scaledRadius/TerrainPatchWidth*2; if (width%2 == 0){ // must be odd width++; } std::cout << "RADIUS " << radius << " Width " << width << std::endl; std::cout << "XYZ " << solarX << " " << solarY << " " << solarZ << std::endl; int centerX = 0; int centerY = 0 + scaledRadius; int centerZ = 0; double magnitude; faces[P_TOP].resize(width); for (size_t i = 0; i < faces[P_TOP].size(); i++){ faces[P_TOP][i].resize(width); for (size_t j = 0; j < faces[P_TOP][i].size(); j++){ faces[P_TOP][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lody = centerY; lodz = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); faces[P_TOP][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_TOP); while(!(faces[P_TOP][i][j]->CreateMesh())); //load all of the quadtrees } } centerX = 0 - scaledRadius; centerY = 0; centerZ = 0; faces[P_LEFT].resize(width); for (size_t i = 0; i < faces[P_LEFT].size(); i++){ faces[P_LEFT][i].resize(width); for (size_t j = 0; j < faces[P_LEFT][i].size(); j++){ faces[P_LEFT][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = centerX; lody = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lodz = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); faces[P_LEFT][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_LEFT); while(!(faces[P_LEFT][i][j]->CreateMesh())); //load all of the quadtrees } } centerX = 0 + scaledRadius; centerY = 0; centerZ = 0; faces[P_RIGHT].resize(width); for (size_t i = 0; i < faces[P_RIGHT].size(); i++){ faces[P_RIGHT][i].resize(width); for (size_t j = 0; j < faces[P_RIGHT][i].size(); j++){ faces[P_RIGHT][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = centerX; lody = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lodz = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); faces[P_RIGHT][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_RIGHT); while(!(faces[P_RIGHT][i][j]->CreateMesh())); //load all of the quadtrees } } centerX = 0; centerY = 0; centerZ = 0 + scaledRadius; faces[P_FRONT].resize(width); for (size_t i = 0; i < faces[P_FRONT].size(); i++){ faces[P_FRONT][i].resize(width); for (size_t j = 0; j < faces[P_FRONT][i].size(); j++){ faces[P_FRONT][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lody = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lodz = centerZ; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); //TopFace[i][j]->Initialize(((double)lodx/magnitude)*radius, ((double)lody/magnitude)*radius, ((double)lodz/magnitude)*radius, centerX, centerY, centerZ); faces[P_FRONT][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_FRONT); while(!(faces[P_FRONT][i][j]->CreateMesh())); //load all of the quadtrees } } centerX = 0; centerY = 0; centerZ = 0 - scaledRadius; faces[P_BACK].resize(width); for (size_t i = 0; i < faces[P_BACK].size(); i++){ faces[P_BACK][i].resize(width); for (size_t j = 0; j < faces[P_BACK][i].size(); j++){ faces[P_BACK][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lody = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lodz = centerZ; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); faces[P_BACK][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_BACK); while(!(faces[P_BACK][i][j]->CreateMesh())); //load all of the quadtrees } } centerX = 0; centerY = 0 - scaledRadius; centerZ = 0; faces[P_BOTTOM].resize(width); for (size_t i = 0; i < faces[P_BOTTOM].size(); i++){ printf("%d ", i); faces[P_BOTTOM][i].resize(width); for (size_t j = 0; j < faces[P_BOTTOM][i].size(); j++){ faces[P_BOTTOM][i][j] = new TerrainPatch(TerrainPatchWidth); lodx = 0 + (j - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; lody = centerY; lodz = 0 + (i - width/2) * TerrainPatchWidth - TerrainPatchWidth/2; magnitude = sqrt(lodx*lodx + lody*lody + lodz*lodz); faces[P_BOTTOM][i][j]->Initialize(lodx, lody, lodz, wx, wy, wz, radius, P_BOTTOM); while(!(faces[P_BOTTOM][i][j]->CreateMesh())); //load all of the quadtrees } } } void Planet::loadData(nString filePath, bool ignoreBiomes) { loadProperties(filePath + "properties.ini"); saveProperties(filePath + "properties.ini"); //save em to update them TerrainGenerator* generator = GameManager::terrainGenerator; GameManager::planet = this; vg::TextureCache* textureCache = GameManager::textureCache; if (!ignoreBiomes){ if (fileManager.loadNoiseFunctions((filePath + "Noise/TerrainNoise.SOANOISE").c_str(), 0, this)) { pError("Failed to load terrain noise functions!"); exit(75); } if (fileManager.loadNoiseFunctions((filePath + "Noise/MandatoryNoise.SOANOISE").c_str(), 1, this)) { pError("Failed to load mandatory noise functions!"); exit(76); } } if (fileManager.loadFloraData(this, filePath)) { pError("Failed to load flora data!"); exit(77); } if (fileManager.loadAllTreeData(this, filePath)) { pError("Failed to load tree data"); exit(78); } if (!ignoreBiomes){ if (fileManager.loadBiomeData(this, filePath)) { pError("Failed to load biome data!"); exit(79); } } //Uncomment to save all trees after loading, for file conversions /*for (size_t i = 0; i < treeTypeVec.size(); i++){ fileManager.SaveTreeData(treeTypeVec[i]); }*/ fileManager.saveBiomeData(this, filePath); sunColorMapTexture = textureCache->addTexture(filePath + "/Sky/sunColor.png", &SamplerState::LINEAR_CLAMP_MIPMAP); #define MAP_WIDTH 256 GLubyte buffer[MAP_WIDTH * MAP_WIDTH][3]; if (!ignoreBiomes){ glActiveTexture(GL_TEXTURE8); glBindTexture(GL_TEXTURE_2D, GameManager::planet->biomeMapTexture.id); glGetTexImage(GL_TEXTURE_2D, 0, GL_BGR, GL_UNSIGNED_BYTE, buffer); //convert rgb values into a hex int for (int i = 0; i < MAP_WIDTH; i++){ for (int j = 0; j < MAP_WIDTH; j++){ int hexcode = 0; hexcode |= (((int)buffer[i * MAP_WIDTH + j][2]) << 16); //converts bgr to rgb hexcode |= (((int)buffer[i * MAP_WIDTH + j][1]) << 8); hexcode |= ((int)buffer[i * MAP_WIDTH + j][0]); generator->BiomeMap[i][j] = hexcode; } } } ColorRGB8* biomeMap = GameManager::texturePackLoader->getColorMap("biome"); ColorRGB8* waterMap = GameManager::texturePackLoader->getColorMap("water"); //color map! glBindTexture(GL_TEXTURE_2D, GameManager::planet->colorMapTexture.id); glGetTexImage(GL_TEXTURE_2D, 0, GL_BGR, GL_UNSIGNED_BYTE, buffer); for (int y = 0; y < MAP_WIDTH; y++){ for (int x = 0; x < MAP_WIDTH; x++) { biomeMap[(MAP_WIDTH - y - 1) * MAP_WIDTH + x] = ColorRGB8(buffer[y * MAP_WIDTH + x][2], //convert bgr to rgb buffer[y * MAP_WIDTH + x][1], buffer[y * MAP_WIDTH + x][0]); } } glBindTexture(GL_TEXTURE_2D, GameManager::planet->waterColorMapTexture.id); glGetTexImage(GL_TEXTURE_2D, 0, GL_BGR, GL_UNSIGNED_BYTE, buffer); for (int y = 0; y < MAP_WIDTH; y++){ for (int x = 0; x < MAP_WIDTH; x++) { waterMap[(MAP_WIDTH - y - 1) * MAP_WIDTH + x] = ColorRGB8(buffer[y * MAP_WIDTH + x][2], //convert bgr to rgb buffer[y * MAP_WIDTH + x][1], buffer[y * MAP_WIDTH + x][0]); } } } void Planet::saveProperties(nString filePath) { std::vector > iniValues; std::vector iniSections; iniSections.push_back(""); iniValues.push_back(std::vector()); iniSections.push_back("Properties"); iniValues.push_back(std::vector()); iniValues.back().push_back(IniValue("x", 0)); iniValues.back().push_back(IniValue("y", 0)); iniValues.back().push_back(IniValue("z", 0)); iniValues.back().push_back(IniValue("radius", radius)); iniValues.back().push_back(IniValue("gravityConstant", gravityConstant)); iniValues.back().push_back(IniValue("density", density)); iniValues.back().push_back(IniValue("axialTilt", axialZTilt)); iniSections.push_back("Climate"); iniValues.push_back(std::vector()); iniValues.back().push_back(IniValue("baseTemperature", baseTemperature)); iniValues.back().push_back(IniValue("minCelsius", minCelsius)); iniValues.back().push_back(IniValue("maxCelsius", maxCelsius)); iniValues.back().push_back(IniValue("baseHumidity", baseRainfall)); iniValues.back().push_back(IniValue("minHumidity", minHumidity)); iniValues.back().push_back(IniValue("maxHumidity", maxHumidity)); fileManager.saveIniFile(filePath, iniValues, iniSections); } void Planet::loadProperties(nString filePath) { std::vector < std::vector > iniValues; std::vector iniSections; fileManager.loadIniFile(filePath, iniValues, iniSections); int iVal; IniValue *iniVal; for (size_t i = 0; i < iniSections.size(); i++){ for (size_t j = 0; j < iniValues[i].size(); j++){ iniVal = &(iniValues[i][j]); iVal = fileManager.getIniVal(iniVal->key); switch (iVal){ case INI_X: solarX = iniVal->getInt(); break; case INI_Y: solarY = iniVal->getInt(); break; case INI_Z: solarZ = iniVal->getInt(); break; case INI_DENSITY: density = iniVal->getFloat(); break; case INI_GRAVITYCONSTANT: gravityConstant = iniVal->getFloat(); break; case INI_BASETEMPERATURE: baseTemperature = iniVal->getInt(); break; case INI_BASEHUMIDITY: baseRainfall = iniVal->getInt(); break; case INI_AXIALTILT: axialZTilt = iniVal->getFloat(); break; case INI_MINCELSIUS: minCelsius = iniVal->getFloat(); break; case INI_MAXCELSIUS: maxCelsius = iniVal->getFloat(); break; case INI_MINHUMIDITY: minHumidity = iniVal->getFloat(); break; case INI_MAXHUMIDITY: maxHumidity = iniVal->getFloat(); break; case INI_RADIUS: radius = iniVal->getFloat(); scaledRadius = (radius - radius%CHUNK_WIDTH) / planetScale; int width = scaledRadius / TerrainPatchWidth * 2; if (width % 2 == 0){ // must be odd width++; } scaledRadius = (width*TerrainPatchWidth) / 2; radius = (int)(scaledRadius*planetScale); break; } } } } void Planet::saveData() { fileManager.saveBiomeData(this, "World/"); fileManager.saveAllBiomes(this); } void Planet::rotationUpdate() { float rotationInput = GameManager::inputManager->getAxis(INPUT_PLANET_ROTATION); if (std::abs(rotationInput) > 0) { rotationTheta += rotationInput * 0.01 * physSpeedFactor; }else{ rotationTheta += 0.00002*physSpeedFactor;//0.00001 } if (rotationTheta > 3.14){ rotationTheta = -3.14; }else if (rotationTheta < -3.14){ rotationTheta = 3.14; } glm::vec3 EulerAngles(0, rotationTheta, axialZTilt); rotateQuaternion = glm::quat(EulerAngles); //gameToGl.enqueue(OMessage(GL_M_UPDATEPLANET, (void *)(new PlanetUpdateMessage(glm::toMat4(rotateQuaternion))))); rotationMatrix = glm::toMat4(rotateQuaternion); invRotationMatrix = glm::inverse(rotationMatrix); } void Planet::draw(float theta, const Camera* camera, glm::vec3 lightPos, GLfloat sunVal, float fadeDistance, bool connectedToPlanet) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, terrainTexture.id); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, waterNormalTexture.id); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, biomeMapTexture.id); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, waterColorMapTexture.id); glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_2D, waterNoiseTexture.id); glActiveTexture(GL_TEXTURE6); glBindTexture(GL_TEXTURE_2D, sunColorMapTexture.id); glActiveTexture(GL_TEXTURE7); glBindTexture(GL_TEXTURE_2D, colorMapTexture.id); closestTerrainPatchDistance = 999999999999.0; const f64v3& playerPos = camera->getPosition(); glm::dvec3 rotPlayerPos; glm::dvec3 nPlayerPos; glm::vec3 rotLightPos; rotLightPos = glm::vec3((GameManager::planet->invRotationMatrix) * glm::vec4(lightPos, 1.0)); bool onPlanet; if (connectedToPlanet){ rotPlayerPos = playerPos; nPlayerPos = playerPos; onPlanet = 1; }else{ rotPlayerPos = glm::dvec3((glm::dmat4(GameManager::planet->invRotationMatrix)) * glm::dvec4(playerPos, 1.0)); nPlayerPos = playerPos; onPlanet = 0; } f32m4 VP = camera->getProjectionMatrix() * camera->getViewMatrix(); drawGround(theta, camera, VP, rotLightPos, nPlayerPos, rotPlayerPos, fadeDistance, onPlanet); } void Planet::drawTrees(const glm::mat4 &VP, const glm::dvec3 &PlayerPos, GLfloat sunVal) { glm::vec3 worldUp = glm::vec3(glm::normalize(PlayerPos)); // glDisable(GL_CULL_FACE); vg::GLProgram* program = GameManager::glProgramManager->getProgram("TreeBillboard"); program->use(); program->enableVertexAttribArrays(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, treeTrunkTexture1.id); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, normalLeavesTexture.id); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, pineLeavesTexture.id); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, mushroomCapTexture.id); glUniform3f(program->getUniform("worldUp"), worldUp.x, worldUp.y, worldUp.z); glUniform1f(program->getUniform("FadeDistance"), (GLfloat)((csGridWidth / 2) * CHUNK_WIDTH)*invPlanetScale); glUniform1f(program->getUniform("sunVal"), sunVal); for (size_t f = 0; f < 6; f++){ for (size_t i = 0; i < drawList[f].size(); i++){ if (drawList[f][i]->treeIndexSize) TerrainPatch::DrawTrees(drawList[f][i], program, PlayerPos, VP); } } program->disableVertexAttribArrays(); program->unuse(); } void Planet::drawGround(float theta, const Camera* camera, const glm::mat4 &VP, glm::vec3 lightPos, const glm::dvec3 &PlayerPos, const glm::dvec3 &rotPlayerPos, float fadeDistance, bool onPlanet) { vg::GLProgram* shader = GameManager::glProgramManager->getProgram("GroundFromSpace"); shader->use(); const i32 textureUnits[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; glUniform1iv(glGetUniformLocation(shader->getID(), "unTextures"), 6, textureUnits); glUniform1i(glGetUniformLocation(shader->getID(), "unTexSunGradient"), textureUnits[6]); glUniform1i(glGetUniformLocation(shader->getID(), "unTexColor"), textureUnits[7]); f32 m_Kr4PI = atmosphere.m_Kr*4.0f*M_PI; f32 m_Km4PI = atmosphere.m_Km*4.0f*M_PI; f32 m_fScale = 1 / (atmosphere.radius - atmosphere.planetRadius); glUniform3f(shader->getUniform("unCameraPos"), (float)rotPlayerPos.x, (float)rotPlayerPos.y, (float)rotPlayerPos.z); glUniform3f(shader->getUniform("unLightPos"), lightPos.x, lightPos.y, lightPos.z); glUniform3f(shader->getUniform("unInvWavelength"), 1 / atmosphere.m_fWavelength4[0], 1 / atmosphere.m_fWavelength4[1], 1 / atmosphere.m_fWavelength4[2]); glUniform1f(shader->getUniform("unSpecularExponent"), graphicsOptions.specularExponent); glUniform1f(shader->getUniform("unSpecularIntensity"), graphicsOptions.specularIntensity); #define MAX_FREEZE_TEMP 255.0f glUniform1f(shader->getUniform("unCameraHeight2"), glm::length(PlayerPos)*glm::length(PlayerPos)); glUniform1f(shader->getUniform("unOuterRadius"), atmosphere.radius); glUniform1f(shader->getUniform("unOuterRadius2"), atmosphere.radius*atmosphere.radius); glUniform1f(shader->getUniform("unInnerRadius"), atmosphere.planetRadius); glUniform1f(shader->getUniform("unKrESun"), atmosphere.m_Kr*atmosphere.m_ESun); glUniform1f(shader->getUniform("unKmESun"), atmosphere.m_Km*atmosphere.m_ESun); glUniform1f(shader->getUniform("unKr4PI"), m_Kr4PI); glUniform1f(shader->getUniform("unKm4PI"), m_Km4PI); glUniform1f(shader->getUniform("unScale"), m_fScale); glUniform1f(shader->getUniform("unScaleDepth"), atmosphere.m_fRayleighScaleDepth); glUniform1f(shader->getUniform("unScaleOverScaleDepth"), m_fScale / atmosphere.m_fRayleighScaleDepth); glUniform1f(shader->getUniform("unNumSamplesF"), atmosphere.fSamples); glUniform1i(shader->getUniform("unNumSamples"), atmosphere.nSamples); // glUniform1f(shader->getUniform("dt"), bdt); What was this used for? glUniform1f(shader->getUniform("unG"), atmosphere.m_g); glUniform1f(shader->getUniform("unG2"), atmosphere.m_g * atmosphere.m_g); glUniform1f(shader->getUniform("unSecColorMult"), graphicsOptions.secColorMult); shader->enableVertexAttribArrays(); const ui32& mvpID = shader->getUniform("unWVP"); const ui32& worldOffsetID = shader->getUniform("unWorld"); for (size_t i = 0; i < drawList[0].size(); i++){ TerrainPatch::Draw(drawList[0][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } for (size_t i = 0; i < drawList[2].size(); i++){ TerrainPatch::Draw(drawList[2][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } for (size_t i = 0; i < drawList[4].size(); i++){ TerrainPatch::Draw(drawList[4][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } glFrontFace(GL_CW); for (size_t i = 0; i < drawList[5].size(); i++){ TerrainPatch::Draw(drawList[5][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } for (size_t i = 0; i < drawList[1].size(); i++){ TerrainPatch::Draw(drawList[1][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } for (size_t i = 0; i < drawList[3].size(); i++){ TerrainPatch::Draw(drawList[3][i], camera, PlayerPos, rotPlayerPos, VP, mvpID, worldOffsetID, onPlanet); } glFrontFace(GL_CCW); shader->disableVertexAttribArrays(); shader->unuse(); } void Planet::updateLODs(glm::dvec3 &worldPosition, GLuint maxTicks) { static int k = 0; glm::dvec3 rWorldPosition = worldPosition; //glm::dvec3 rWorldPosition = glm::dvec3((glm::dmat4(GameManager::planet->invRotationMatrix)) * glm::dvec4(worldPosition, 1.0)); int mx = (int)rWorldPosition.x, my = (int)rWorldPosition.y, mz = (int)rWorldPosition.z; for (int n = 0; n < 6; n++){ for (unsigned int a = 0; a < faces[n].size(); a++){ for (unsigned int b = 0; b < faces[n][a].size(); b++){ if (faces[n][a][b]->update(mx, my, mz)){ if (faces[n][a][b]->updateVecIndex == -1){ faces[n][a][b]->updateVecIndex = LODUpdateList.size(); LODUpdateList.push_back(faces[n][a][b]); } } } } } GLuint startTicks = SDL_GetTicks(); if (LODUpdateList.empty()) return; //cout << LODUpdateList.size() << " "; // GLuint starttime = SDL_GetTicks(); TerrainPatch *lod; int wx = (0+CHUNK_WIDTH*csGridWidth/2); int wz = (0+CHUNK_WIDTH*csGridWidth/2); int size = LODUpdateList.size()-1; sortUpdateList(); //sorts every update, but insertion sort is fast for (int i = size; LODUpdateList.size(); i--) { lod = LODUpdateList.back(); if (lod->CreateMesh()){ LODUpdateList.pop_back(); lod->updateVecIndex = -1; } if (SDL_GetTicks() - startTicks > maxTicks){ return; } } k++; // cout << SDL_GetTicks() - starttime << " L\n"; } void RecursiveFlagLOD(TerrainPatch *lod){ lod->updateCode = 1; if (lod->lods[0]){ for(int i = 0; i < 4; i++){ RecursiveFlagLOD(lod->lods[i]); } } } void Planet::flagTerrainForRebuild() { for (int n = 0; n < 6; n++){ for (unsigned int a = 0; a < faces[n].size(); a++){ for (unsigned int b = 0; b < faces[n][a].size(); b++){ RecursiveFlagLOD(faces[n][a][b]); if (faces[n][a][b]->updateVecIndex == -1){ faces[n][a][b]->updateVecIndex = LODUpdateList.size(); LODUpdateList.push_back(faces[n][a][b]); } } } } } void Planet::sortUpdateList() { TerrainPatch *temp; int j; for (unsigned int i = 1; i < LODUpdateList.size(); i++) { temp = LODUpdateList[i]; for (j = i - 1; (j >= 0) && (temp->distance > LODUpdateList[j]->distance); j-- ){ LODUpdateList[j+1] = LODUpdateList[j]; LODUpdateList[j+1]->vecIndex = j+1; } LODUpdateList[j+1] = temp; LODUpdateList[j+1]->vecIndex = j+1; } } void Planet::destroy() { for (size_t i = 0; i < 6; i++){ for (size_t j = 0; j < faces[i].size(); j++){ for (size_t k = 0; k < faces[i][j].size(); k++){ delete faces[i][j][k]; } } faces[i].clear(); } for (size_t i = 0; i < allBiomesLookupVector.size(); i++){ delete allBiomesLookupVector[i]; } allBiomesLookupVector.clear(); for (size_t i = 0; i < floraNoiseFunctions.size(); i++){ delete floraNoiseFunctions[i]; } floraNoiseFunctions.clear(); for (size_t i = 0; i < treeTypeVec.size(); i++){ delete treeTypeVec[i]; } treeTypeVec.clear(); for (size_t i = 0; i < floraTypeVec.size(); i++){ delete floraTypeVec[i]; } floraTypeVec.clear(); vg::TextureCache* textureCache = GameManager::textureCache; textureCache->freeTexture(biomeMapTexture); textureCache->freeTexture(sunColorMapTexture); textureCache->freeTexture(sunColorMapTexture); textureCache->freeTexture(waterColorMapTexture); } void Planet::clearBiomes() //MEMORY LEAKS ARE ON PURPOSE. NOT MEANT FOR FINAL GAME { bindex = 0; allBiomesLookupVector.clear(); mainBiomesVector.clear(); childBiomesVector.clear(); baseBiomesLookupMap.clear(); } void Planet::addBaseBiome(Biome *baseBiome, int mapColor) { baseBiome->vecIndex = bindex++; baseBiomesLookupMap.insert(std::make_pair(mapColor, baseBiome)); allBiomesLookupVector.push_back(baseBiome); //biome = new Biome; //freed in chunkmanager destructor //biome->name = name; //biome->r = color.r; //biome->g = color.g; //biome->b = color.b; //biome->surfaceBlock = surfaceBlock; //biome->treeChance = treeChance; //biome->vecIndex = index++; //biome->possibleTrees = treetypes; //biome->treeProbabilities = treeprobs; //biome->possibleFlora = floratypes; //biome->floraProbabilities = floraprobs; //biomesLookupMap.insert(make_pair(mapColor, biome)); //biomesLookupVector.push_back(biome); } void Planet::addMainBiome(Biome *mainBiome) { mainBiome->vecIndex = bindex++; allBiomesLookupVector.push_back(mainBiome); mainBiomesVector.push_back(mainBiome); } void Planet::addChildBiome(Biome *childBiome) { childBiome->vecIndex = bindex++; allBiomesLookupVector.push_back(childBiome); childBiomesVector.push_back(childBiome); } double Planet::getGravityAccel(double dist) { if (dist < radius) dist = radius; dist = dist*0.5; //convert to meters return (mass*M_G/(dist*dist)) * 0.01666 * 0.01666 * 2.0; //divide by 60 twice to account for frames } double Planet::getAirFrictionForce(double dist, double velocity) { if (dist > atmosphere.radius) return 0.0; //no friction in space duh velocity *= 0.5; //reduce to m/s return 0.25*getAirDensity(dist)*velocity*velocity; //assuming drag of 0.5 } //earths density is 1.2 kg/m3 //barometric formula double Planet::getAirDensity(double dist) { double maxDensity = 1.2; double mult; if (dist <= radius) return maxDensity; if (dist >= atmosphere.radius) return 0; mult = (dist-radius)/(atmosphere.radius-radius); return (pow(0.8, mult*20.0)-0.0115) * maxDensity; } Atmosphere::Atmosphere() { m_Kr = 0.0025f; // Rayleigh scattering constant m_Km = 0.0020f; // Mie scattering constant m_ESun = 25.0f; // Sun brightness constant m_g = -0.990f; // The Mie phase asymmetry factor m_fExposure = 2.0f; m_fRayleighScaleDepth = 0.25f; //0.25 m_fWavelength[0] = 0.650f; // 650 nm for red m_fWavelength[1] = 0.570f; // 570 nm for green m_fWavelength[2] = 0.475f; // 475 nm for blue m_fWavelength4[0] = powf(m_fWavelength[0], 4.0f); m_fWavelength4[1] = powf(m_fWavelength[1], 4.0f); m_fWavelength4[2] = powf(m_fWavelength[2], 4.0f); nSamples = 3; fSamples = (float)nSamples; } void Atmosphere::initialize(nString filePath, float PlanetRadius) { if (filePath.empty()){ m_Kr = 0.0025; m_Km = 0.0020; m_ESun = 25.0; m_g = -0.990; m_fExposure = 2.0; m_fWavelength[0] = 0.650; m_fWavelength[1] = 0.570; m_fWavelength[2] = 0.475; nSamples = 3; fSamples = (float)nSamples; m_fWavelength4[0] = powf(m_fWavelength[0], 4.0f); m_fWavelength4[1] = powf(m_fWavelength[1], 4.0f); m_fWavelength4[2] = powf(m_fWavelength[2], 4.0f); } else{ loadProperties(filePath); } radius = PlanetRadius*(1.025); planetRadius = PlanetRadius; //std::cout << "Loading Objects/icosphere.obj... "; //objectLoader.load("Objects/icosphere.obj", vertices, indices); //std::cout << "Done!\n"; glGenBuffers(1, &(vboID)); // Create the buffer ID glBindBuffer(GL_ARRAY_BUFFER, vboID); // Bind the buffer (vertex array data) glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(ColorVertex), NULL, GL_STATIC_DRAW); glGenBuffers(1, &(vboIndexID)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * sizeof(ColorVertex), &(vertices[0].position)); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indices.size() * sizeof(GLushort), &(indices[0])); indexSize = indices.size(); vertices.clear(); indices.clear(); } void Atmosphere::loadProperties(nString filePath) { std::vector < std::vector > iniValues; std::vector iniSections; fileManager.loadIniFile(filePath, iniValues, iniSections); int iVal; IniValue *iniVal; for (size_t i = 0; i < iniSections.size(); i++){ for (size_t j = 0; j < iniValues[i].size(); j++){ iniVal = &(iniValues[i][j]); iVal = fileManager.getIniVal(iniVal->key); switch (iVal){ case INI_M_KR: m_Kr = iniVal->getFloat(); break; case INI_M_KM: m_Km = iniVal->getFloat(); break; case INI_M_ESUN: m_ESun = iniVal->getFloat(); break; case INI_M_G: m_g = iniVal->getFloat(); break; case INI_M_EXP: m_fExposure = iniVal->getFloat(); break; case INI_WAVELENGTHR: m_fWavelength[0] = iniVal->getFloat(); break; case INI_WAVELENGTHG: m_fWavelength[1] = iniVal->getFloat(); break; case INI_WAVELENGTHB: m_fWavelength[2] = iniVal->getFloat(); break; case INI_NSAMPLES: nSamples = iniVal->getInt(); break; } } } fSamples = (float)nSamples; m_fWavelength4[0] = powf(m_fWavelength[0], 4.0f); m_fWavelength4[1] = powf(m_fWavelength[1], 4.0f); m_fWavelength4[2] = powf(m_fWavelength[2], 4.0f); } void Atmosphere::draw(float theta, const glm::mat4 &MVP, glm::vec3 lightPos, const glm::dvec3 &ppos) { vg::GLProgram* shader = GameManager::glProgramManager->getProgram("Sky"); shader->use(); glm::mat4 GlobalModelMatrix(1.0); GlobalModelMatrix[0][0] = radius; GlobalModelMatrix[1][1] = radius; GlobalModelMatrix[2][2] = radius; GlobalModelMatrix[3][0] = (f32)-ppos.x; GlobalModelMatrix[3][1] = (f32)-ppos.y; GlobalModelMatrix[3][2] = (f32)-ppos.z; // Have to rotate it and draw it again to make a sphere f32v3 EulerAngles(M_PI, 0, 0); f32m4 RotationMatrix = glm::toMat4(glm::quat(EulerAngles)); f32m4 MVPr = MVP * GlobalModelMatrix; f32m4 M = GlobalModelMatrix; f32 m_Kr4PI = m_Kr * 4.0f* M_PI; f32 m_Km4PI = m_Km * 4.0f* M_PI; f32 m_fScale = 1.0 / (radius - planetRadius); glUniformMatrix4fv(shader->getUniform("unWVP"), 1, GL_FALSE, &MVPr[0][0]); glUniform3f(shader->getUniform("unCameraPos"), (float)ppos.x, (float)ppos.y, (float)ppos.z); glUniform3f(shader->getUniform("unLightPos"), lightPos.x, lightPos.y, lightPos.z); glUniform3f(shader->getUniform("unInvWavelength"), 1 / m_fWavelength4[0], 1 / m_fWavelength4[1], 1 / m_fWavelength4[2]); glUniform1f(shader->getUniform("unCameraHeight2"), glm::length(ppos) * glm::length(ppos)); glUniform1f(shader->getUniform("unOuterRadius"), radius); glUniform1f(shader->getUniform("unOuterRadius2"), radius * radius); glUniform1f(shader->getUniform("unInnerRadius"), planetRadius); glUniform1f(shader->getUniform("unKrESun"), m_Kr*m_ESun); glUniform1f(shader->getUniform("unKmESun"), m_Km*m_ESun); glUniform1f(shader->getUniform("unKr4PI"), m_Kr4PI); glUniform1f(shader->getUniform("unKm4PI"), m_Km4PI); glUniform1f(shader->getUniform("unScale"), m_fScale); glUniform1f(shader->getUniform("unScaleDepth"), m_fRayleighScaleDepth); glUniform1f(shader->getUniform("unScaleOverScaleDepth"), m_fScale/m_fRayleighScaleDepth); glUniform1f(shader->getUniform("unG"), m_g); glUniform1f(shader->getUniform("unG2"), m_g*m_g); glUniform1i(shader->getUniform("unNumSamples"), nSamples); glUniform1f(shader->getUniform("unNumSamplesF"), fSamples); glBindBuffer(GL_ARRAY_BUFFER, vboID); shader->enableVertexAttribArrays(); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ColorVertex), (void*)0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexID); glDrawElements(GL_TRIANGLES, indexSize, GL_UNSIGNED_SHORT, 0); shader->disableVertexAttribArrays(); shader->unuse(); } ================================================ FILE: SoA/Planet.h ================================================ #pragma once #include #include #include #include #include #include //#include "OpenGLStructs.h" //#include "Texture2d.h" //#include "WorldStructs.h" #include "Vertex.h" #include "Biome.h" #define P_TOP 0 #define P_LEFT 1 #define P_RIGHT 2 #define P_FRONT 3 #define P_BACK 4 #define P_BOTTOM 5 struct TreeType; struct PlantType; class Camera; class NoiseInfo; namespace vg=vorb::graphics; struct AtmosphereProperties { public: f32 rayleighConstant; f32 rayleighScaleDepth; f32 mieConstant; f32 sunIntensity; f32 mieAsymmetry; f32v3 lightWavelengths; }; class Atmosphere { public: Atmosphere(); void initialize(nString filePath, f32 PlanetRadius); void loadProperties(nString filePath); void draw(f32 theta, const f32m4& MVP, f32v3 lightPos, const f64v3& ppos); std::vector vertices; std::vector indices; ui32 vboID, vbo2ID, vboIndexID, vboIndexID2; ui32 indexSize; f64 radius; f64 planetRadius; f32 m_fWavelength[3], m_fWavelength4[3]; f32 m_Kr; // Rayleigh scattering constant f32 m_Km; // Mie scattering constant f32 m_ESun; // Sun brightness constant f32 m_g; // The Mie phase asymmetry factor f32 m_fExposure; f32 m_fRayleighScaleDepth; f32 fSamples; i32 nSamples; i32 debugIndex; }; class Planet { public: Planet(); ~Planet(); void clearMeshes(); void initialize(nString filePath); void initializeTerrain(const f64v3& startPosition); void destroy(); void loadData(nString filePath, bool ignoreBiomes); void saveData(); void draw(f32 theta, const Camera* camera, f32v3 lightPos, f32 sunVal, f32 fadeDistance, bool connectedToPlanet); void drawTrees(const f32m4& VP, const f64v3& PlayerPos, f32 sunVal); void drawGround(f32 theta, const Camera* camera, const f32m4& VP, f32v3 lightPos, const f64v3& PlayerPos, const f64v3& rotPlayerPos, float fadeDistance, bool onPlanet); void updateLODs(f64v3& worldPosition, ui32 maxTicks); void rotationUpdate(); void flagTerrainForRebuild(); void sortUpdateList(); void saveProperties(nString filePath); void loadProperties(nString filePath); f64 getGravityAccel(f64 dist); f64 getAirFrictionForce(f64 dist, f64 velocity); f64 getAirDensity(f64 dist); i32 radius; i32 scaledRadius; i32 solarX, solarY, solarZ; i32 facecsGridWidth; i32 baseTemperature; i32 baseRainfall; f32 minHumidity, maxHumidity; f32 minCelsius, maxCelsius; f64 axialZTilt; f64 gravityConstant; f64 volume; f64 mass; f64 density; f64 rotationTheta; f64v3 northAxis; f32m4 rotationMatrix; f32m4 invRotationMatrix; // GM/r2 std::vector< std::vector > faces[6]; std::vector LODUpdateList; std::vector drawList[6]; // double axialZTilt[66]; void clearBiomes(); void addBaseBiome(Biome* baseBiome, i32 mapColor); void addMainBiome(Biome* mainBiome); void addChildBiome(Biome* childBiome); vg::Texture biomeMapTexture; vg::Texture colorMapTexture; vg::Texture sunColorMapTexture; vg::Texture waterColorMapTexture; i32 bindex; std::map baseBiomesLookupMap; std::vector allBiomesLookupVector; std::vector mainBiomesVector; std::vector childBiomesVector; std::vector floraNoiseFunctions; NoiseInfo* stormNoiseFunction; NoiseInfo* sunnyCloudyNoiseFunction; NoiseInfo* cumulusNoiseFunction; std::vector treeTypeVec; std::vector floraTypeVec; i32 maximumDepth; std::vector rockLayers; std::map treeLookupMap; std::map floraLookupMap; Atmosphere atmosphere; nString dirName; nString biomeMapFileName; nString colorMapFileName; nString waterColorMapFileName; f32q axisQuaternion; f32q rotateQuaternion; }; ================================================ FILE: SoA/PlanetGenData.cpp ================================================ #include "stdafx.h" #include "PlanetGenData.h" KEG_TYPE_DEF_SAME_NAME(BlockLayerKegProperties, kt) { using namespace keg; KEG_TYPE_INIT_ADD_MEMBER(kt, BlockLayerKegProperties, block, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, BlockLayerKegProperties, surface, STRING); kt.addValue("width", Value::basic(offsetof(BlockLayerKegProperties, width), BasicType::UI32)); } KEG_TYPE_DEF_SAME_NAME(LiquidColorKegProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, LiquidColorKegProperties, colorPath, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, LiquidColorKegProperties, texturePath, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, LiquidColorKegProperties, tint, UI8_V3); KEG_TYPE_INIT_ADD_MEMBER(kt, LiquidColorKegProperties, depthScale, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, LiquidColorKegProperties, freezeTemp, F32); } KEG_TYPE_DEF_SAME_NAME(TerrainColorKegProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainColorKegProperties, colorPath, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainColorKegProperties, grassTexturePath, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainColorKegProperties, rockTexturePath, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, TerrainColorKegProperties, tint, UI8_V3); } ================================================ FILE: SoA/PlanetGenData.h ================================================ /// /// PlanetData.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 5 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Structs for planetary data /// #pragma once #ifndef PlanetData_h__ #define PlanetData_h__ #include #include #include #include #include #include "Noise.h" #include "Biome.h" DECL_VG(class GLProgram; class BitmapResource); struct LiquidColorKegProperties { LiquidColorKegProperties(): colorPath(""), texturePath(""), tint(255, 255, 255), depthScale(1000.0f), freezeTemp(-1.0f) {} nString colorPath; nString texturePath; ColorRGB8 tint; f32 depthScale; f32 freezeTemp; }; KEG_TYPE_DECL(LiquidColorKegProperties); struct TerrainColorKegProperties { TerrainColorKegProperties(): colorPath(""), grassTexturePath(""), rockTexturePath(""), tint(255, 255, 255) {} nString colorPath; nString grassTexturePath; nString rockTexturePath; ColorRGB8 tint; }; KEG_TYPE_DECL(TerrainColorKegProperties); // Must match var names for TreeFruitProperties struct FruitKegProperties { FruitKegProperties(): flora(""), chance(0.0f) {} nString flora = ""; f32v2 chance = f32v2(0.0f); }; // Must match var names for TreeLeafProperties struct LeafKegProperties { LeafKegProperties(): type(TreeLeafType::NONE), block("none"), mushGillBlock("none"), mushCapBlock("none") {} TreeLeafType type; FruitKegProperties fruitProps; // Union based on type union { struct { i32v2 vRadius; i32v2 hRadius; } round; struct { i32v2 oRadius; i32v2 iRadius; i32v2 period; } pine; struct { i32v2 tvRadius; i32v2 thRadius; i32v2 bvRadius; i32v2 bhRadius; i32v2 bLength; i32v2 capWidth; i32v2 gillWidth; FloraInterpType interp; } mushroom; }; // Don't put strings in unions nString block; nString mushGillBlock; nString mushCapBlock; }; // Must match var names for TreeBranchProperties struct BranchKegProperties { BranchKegProperties(): coreWidth(0), barkWidth(0), widthFalloff(0.1f), branchChance(0.0f), angle(0.0f), subBranchAngle(0.0f), changeDirChance(0.0f), coreBlock(""), barkBlock("") {} i32v2 coreWidth; i32v2 barkWidth; f32v2 widthFalloff; f32v2 branchChance; f32v2 angle; f32v2 subBranchAngle; f32v2 changeDirChance; nString coreBlock; nString barkBlock; FruitKegProperties fruitProps; LeafKegProperties leafProps; }; // Must match var names for TreeTrunkProperties struct TrunkKegProperties { TrunkKegProperties(): loc(0.0f), coreWidth(0), barkWidth(0), branchChance(0.0f), changeDirChance(0.0f), coreBlock(""), barkBlock(""), interp(FloraInterpType::HERMITE) {} f32 loc; i32v2 coreWidth; i32v2 barkWidth; f32v2 branchChance; f32v2 changeDirChance; i32v2 slope[2]; nString coreBlock; nString barkBlock; FloraInterpType interp; FruitKegProperties fruitProps; LeafKegProperties leafProps; BranchKegProperties branchProps; }; struct BranchVolumeKegProperties { BranchVolumeKegProperties(): height(0), hRadius(0), vRadius(0), points(0) {} i32v2 height; i32v2 hRadius; i32v2 vRadius; i32v2 points; }; // Must match var names for TreeData struct TreeKegProperties { TreeKegProperties(): id(""), height(0), branchPoints(0), branchStep(0), killMult(2), infRadius(0.0f) {} nString id; i32v2 height; i32v2 branchPoints; i32v2 branchStep; i32v2 killMult; f32v2 infRadius; std::vector branchVolumes; std::vector trunkProps; }; struct FloraKegProperties { FloraKegProperties(): block(""), nextFlora(""), height(1), slope(0), dSlope(0), dir(FloraDir::UP) {} nString id; nString block; nString nextFlora; i32v2 height; i32v2 slope; i32v2 dSlope; FloraDir dir; }; struct BlockLayerKegProperties { BlockLayerKegProperties(): block(""), surface(""), width(0) {} nString block; nString surface; ui32 width; }; KEG_TYPE_DECL(BlockLayerKegProperties); // Info about what blocks a planet needs struct PlanetBlockInitInfo { PlanetBlockInitInfo(): liquidBlockName(""), surfaceBlockName("") {} std::map> biomeFlora; std::map> biomeTrees; std::vector trees; std::vector flora; std::vector blockLayers; nString liquidBlockName; nString surfaceBlockName; }; struct PlanetGenData { PlanetGenData(): terrainColorMap(0), liquidColorMap(0), grassTexture(0), rockTexture(0), liquidTexture(0), liquidTint(255, 255, 255), terrainTint(255, 255, 255), liquidDepthScale(1000.0f), liquidFreezeTemp(-1.0f), tempLatitudeFalloff(0.0f), tempHeightFalloff(0.0f), humLatitudeFalloff(0.0f), humHeightFalloff(0.0f), liquidBlock(0), surfaceBlock(0), radius(0.0) {} vg::Texture terrainColorMap; vg::Texture liquidColorMap; vg::Texture grassTexture; vg::Texture rockTexture; vg::Texture liquidTexture; vg::BitmapResource terrainColorPixels; vg::BitmapResource liquidColorPixels; color3 liquidTint; color3 terrainTint; f32 liquidDepthScale; f32 liquidFreezeTemp; f32 tempLatitudeFalloff; f32 tempHeightFalloff; f32 humLatitudeFalloff; f32 humHeightFalloff; PlanetBlockInitInfo blockInfo; std::vector blockLayers; ui32 liquidBlock; ui32 surfaceBlock; f64 radius; /************************************************************************/ /* Base Noise */ /************************************************************************/ NoiseBase baseTerrainFuncs; NoiseBase tempTerrainFuncs; NoiseBase humTerrainFuncs; /************************************************************************/ /* Flora and Trees */ /************************************************************************/ std::vector flora; std::map floraMap; std::vector trees; std::map treeMap; /************************************************************************/ /* Biomes */ /************************************************************************/ const Biome* baseBiomeLookup[BIOME_MAP_WIDTH][BIOME_MAP_WIDTH]; std::vector baseBiomeInfluenceMap[BIOME_MAP_WIDTH][BIOME_MAP_WIDTH]; std::vector biomes; ///< Biome object storage. DON'T EVER RESIZE AFTER GEN. nString terrainFilePath; }; #endif // PlanetData_h__ ================================================ FILE: SoA/PlanetGenLoader.cpp ================================================ #include "stdafx.h" #include "PlanetGenLoader.h" #include "PlanetGenData.h" #include #include #include #include #include #include #include #include #include "BlockPack.h" #include "Errors.h" #include "Biome.h" typedef ui32 BiomeColorCode; PlanetGenData* PlanetGenLoader::m_defaultGenData = nullptr; struct BiomeKegProperties { Array children; Array blockLayers; Array flora; Array trees; BiomeID id = ""; ColorRGB8 mapColor = ColorRGB8(255, 255, 255); NoiseBase childNoise; ///< For sub biome determination NoiseBase terrainNoise; ///< Modifies terrain directly f64v2 heightRange = f64v2(0.0, 1000.0); f64v2 heightScale = f64v2(0.01, 0.01); f64v2 noiseRange = f64v2(-1.0, 1.0); f64v2 noiseScale = f64v2(10.0, 10.0); nString displayName = "Unknown"; }; KEG_TYPE_DECL(BiomeKegProperties); KEG_TYPE_DEF_SAME_NAME(BiomeKegProperties, kt) { using namespace keg; KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, id, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, displayName, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, mapColor, UI8_V3); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, heightRange, F64_V2); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, heightScale, F64_V2); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, noiseRange, F64_V2); KEG_TYPE_INIT_ADD_MEMBER(kt, BiomeKegProperties, noiseScale, F64_V2); kt.addValue("blockLayers", Value::array(offsetof(BiomeKegProperties, blockLayers), Value::custom(0, "BlockLayer"))); kt.addValue("terrainNoise", Value::custom(offsetof(BiomeKegProperties, terrainNoise), "NoiseBase", false)); kt.addValue("flora", Value::array(offsetof(BiomeKegProperties, flora), Value::custom(0, "BiomeFloraKegProperties"))); kt.addValue("trees", Value::array(offsetof(BiomeKegProperties, trees), Value::custom(0, "BiomeTreeKegProperties"))); kt.addValue("childNoise", Value::custom(offsetof(BiomeKegProperties, childNoise), "NoiseBase", false)); kt.addValue("children", Value::array(offsetof(BiomeKegProperties, children), Value::custom(0, "BiomeKegProperties", false))); } void PlanetGenLoader::init(vio::IOManager* ioManager) { m_iom = ioManager; m_textureCache.init(ioManager); } CALLER_DELETE PlanetGenData* PlanetGenLoader::loadPlanetGenData(const nString& terrainPath) { nString data; m_iom->readFileToString(terrainPath.c_str(), data); keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to load " + terrainPath; context.reader.dispose(); return nullptr; } PlanetGenData* genData = new PlanetGenData; genData->terrainFilePath = terrainPath; nString biomePath = ""; nString floraPath = ""; nString treesPath = ""; // bool didLoadBiomes = false; auto f = makeFunctor([&](Sender, const nString& type, keg::Node value) { // Parse based on type if (type == "biomes") { biomePath = keg::convert(value); } else if (type == "flora") { floraPath = keg::convert(value); } else if (type == "trees") { treesPath = keg::convert(value); } else if (type == "terrainColor") { parseTerrainColor(context, value, genData); } else if (type == "liquidColor") { parseLiquidColor(context, value, genData); } else if (type == "tempLatitudeFalloff") { genData->tempLatitudeFalloff = keg::convert(value); } else if (type == "humLatitudeFalloff") { genData->humLatitudeFalloff = keg::convert(value); } else if (type == "tempHeightFalloff") { genData->tempHeightFalloff = keg::convert(value); } else if (type == "humHeightFalloff") { genData->humHeightFalloff = keg::convert(value); } else if (type == "baseHeight") { parseTerrainFuncs(&genData->baseTerrainFuncs, context, value); } else if (type == "temperature") { parseTerrainFuncs(&genData->tempTerrainFuncs, context, value); } else if (type == "humidity") { parseTerrainFuncs(&genData->humTerrainFuncs, context, value); } else if (type == "blockLayers") { parseBlockLayers(context, value, genData); } else if (type == "liquidBlock") { genData->blockInfo.liquidBlockName = keg::convert(value); } }); context.reader.forAllInMap(node, &f); context.reader.dispose(); if (floraPath.size()) { loadFlora(floraPath, genData); } if (treesPath.size()) { loadTrees(treesPath, genData); } if (biomePath.size()) { loadBiomes(biomePath, genData); } else { // Set default biome for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { genData->baseBiomeLookup[y][x] = &DEFAULT_BIOME; } } } return genData; } PlanetGenData* PlanetGenLoader::getDefaultGenData(vcore::RPCManager* glrpc VORB_MAYBE_UNUSED /* = nullptr */) { // Lazily construct default data if (!m_defaultGenData) { // Allocate data m_defaultGenData = new PlanetGenData; } return m_defaultGenData; } CALLER_DELETE PlanetGenData* PlanetGenLoader::getRandomGenData(f32 radius, vcore::RPCManager* glrpc /* = nullptr */) { // Lazily construct default data // Allocate data PlanetGenData* genData = m_planetGenerator.generateRandomPlanet(SpaceObjectType::PLANET, glrpc); // TODO(Ben): Radius is temporary hacky fix for small planet darkness! if (radius < 15.0) { genData->baseTerrainFuncs.funcs.setData(); } // TODO: Reimplement these as suitable. // TODO(Matthew): Note in EVERY case of using RPC, it may be better if RPC owns the function, as otherwise we can't do non-blocking RPC. // Load textures if (glrpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender, void*) { genData->grassTexture = m_textureCache.addTexture("_shared/terrain_b.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); genData->rockTexture = m_textureCache.addTexture("_shared/terrain_a.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); genData->liquidTexture = m_textureCache.addTexture("_shared/water_a.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); })); glrpc->invoke(&rpc, true); delete rpc.data.f; } else { genData->grassTexture = m_textureCache.addTexture("_shared/terrain_b.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); genData->rockTexture = m_textureCache.addTexture("_shared/terrain_a.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); genData->liquidTexture = m_textureCache.addTexture("_shared/water_a.png", vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); } // Set default biome for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { genData->baseBiomeLookup[y][x] = &DEFAULT_BIOME; } } return genData; } AtmosphereProperties PlanetGenLoader::getRandomAtmosphere() { static std::mt19937 generator(2636); static std::uniform_real_distribution randomWav(0.4f, 0.8f); AtmosphereProperties props; props.waveLength.r = randomWav(generator); props.waveLength.g = randomWav(generator); props.waveLength.b = randomWav(generator); return props; } // Helper function for loadBiomes ui32 recursiveCountBiomes(const BiomeKegProperties& props) { ui32 rv = 1; for (size_t i = 0; i < props.children.size(); i++) { rv += recursiveCountBiomes(props.children[i]); } return rv; } const int FILTER_SIZE = 5; const int FILTER_OFFSET = FILTER_SIZE / 2; float blurFilter[FILTER_SIZE][FILTER_SIZE] = { {0.04f, 0.04f, 0.04f, 0.04f, 0.04f}, {0.04f, 0.04f, 0.04f, 0.04f, 0.04f}, {0.04f, 0.04f, 0.04f, 0.04f, 0.04f}, {0.04f, 0.04f, 0.04f, 0.04f, 0.04f}, {0.04f, 0.04f, 0.04f, 0.04f, 0.04f} }; void blurBiomeMap(const std::vector& bMap, OUT std::vector>& outMap) { /* Very simple blur function with 5x5 box kernel */ outMap.resize(bMap.size()); // Loop through the map for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { auto& b = bMap[y * BIOME_MAP_WIDTH + x]; if (b.b) { // Loop through box filter for (int j = 0; j < FILTER_SIZE; j++) { for (int k = 0; k < FILTER_SIZE; k++) { int xPos = (x - FILTER_OFFSET + k); int yPos = (y - FILTER_OFFSET + j); // Bounds checking if (xPos < 0) { xPos = 0; } else if (xPos >= BIOME_MAP_WIDTH) { xPos = BIOME_MAP_WIDTH - 1; } if (yPos < 0) { yPos = 0; } else if (yPos >= BIOME_MAP_WIDTH) { yPos = BIOME_MAP_WIDTH - 1; } // Get the list of biomes currently in the blurred map auto& biomes = outMap[yPos * BIOME_MAP_WIDTH + xPos]; // See if the current biome is already there auto it = biomes.find(b); // Force modify weight in set with const cast. // It's ok since weight doesn't affect set position, promise! if (it == biomes.end()) { // Add biome and modify weight const_cast(*biomes.insert(b).first).weight = blurFilter[j][k] * b.weight; } else { // Modify existing biome weight const_cast(*it).weight += blurFilter[j][k] * b.weight; } } } } } } } void blurBaseBiomeMap(const Biome* baseBiomeLookup[BIOME_MAP_WIDTH][BIOME_MAP_WIDTH], OUT std::vector>& outMap) { /* Very simple blur function with 5x5 box kernel */ outMap.resize(BIOME_MAP_WIDTH * BIOME_MAP_WIDTH); // Loop through the map for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { auto& b = baseBiomeLookup[y][x]; // Loop through box filter for (int j = 0; j < FILTER_SIZE; j++) { for (int k = 0; k < FILTER_SIZE; k++) { int xPos = (x - FILTER_OFFSET + k); int yPos = (y - FILTER_OFFSET + j); // Bounds checking if (xPos < 0) { xPos = 0; } else if (xPos >= BIOME_MAP_WIDTH) { xPos = BIOME_MAP_WIDTH - 1; } if (yPos < 0) { yPos = 0; } else if (yPos >= BIOME_MAP_WIDTH) { yPos = BIOME_MAP_WIDTH - 1; } // Get the list of biomes currently in the blurred map auto& biomes = outMap[yPos * BIOME_MAP_WIDTH + xPos]; // See if the current biome is already there // TODO(Ben): Better find auto it = biomes.find({ b, 1.0f }); // Force modify weight in set with const cast. // It's ok since weight doesn't affect set position, promise! if (it == biomes.end()) { // Add biome and set biomes.emplace(b, blurFilter[j][k]); } else { // Modify existing biome weight const_cast(*it).weight += blurFilter[j][k]; } } } } } } void recursiveInitBiomes(Biome& biome, const BiomeKegProperties& kp, ui32& biomeCounter, PlanetGenData* genData) { // Copy all biome data biome.id = kp.id; biome.blockLayers.resize(kp.blockLayers.size()); for (size_t i = 0; i < kp.blockLayers.size(); i++) { biome.blockLayers[i] = kp.blockLayers[i]; } biome.displayName = kp.displayName; biome.mapColor = kp.mapColor; biome.heightRange = kp.heightRange; biome.heightScale = kp.heightScale; biome.noiseRange = kp.noiseRange; biome.noiseScale = kp.noiseScale; biome.terrainNoise = kp.terrainNoise; biome.childNoise = kp.childNoise; // Construct vectors in place for flora and trees auto& floraPropList = genData->blockInfo.biomeFlora.insert( std::make_pair(&biome, std::vector())).first->second; auto& treePropList = genData->blockInfo.biomeTrees.insert( std::make_pair(&biome, std::vector())).first->second; // Copy flora data over floraPropList.resize(kp.flora.size()); for (size_t i = 0; i < kp.flora.size(); i++) { floraPropList[i] = kp.flora[i]; } // Copy tree data over treePropList.resize(kp.trees.size()); for (size_t i = 0; i < kp.trees.size(); i++) { treePropList[i] = kp.trees[i]; } // Recurse children biome.children.resize(kp.children.size()); for (size_t i = 0; i < kp.children.size(); i++) { Biome& nextBiome = genData->biomes[biomeCounter++]; recursiveInitBiomes(nextBiome, kp.children[i], biomeCounter, genData); biome.children[i] = &nextBiome; } } // Conditionally parses a value so it can be either a v2 or a single value // When its single, it sets both values of the v2 to that value #define PARSE_V2(type, v) \ if (keg::getType(value) == keg::NodeType::VALUE) { \ keg::evalData((ui8*)&v, &type##Val, value, context); \ v.y = v.x; \ } else { \ keg::evalData((ui8*)&v, &type##v2Val, value, context); \ } void PlanetGenLoader::loadFlora(const nString& filePath, PlanetGenData* genData) { // Read in the file nString data; m_iom->readFileToString(filePath.c_str(), data); // Get the read context and error check keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to load " + filePath; context.reader.dispose(); return; } FloraKegProperties* floraProps; // Custom values, must match PARSE_V2 syntax keg::Value i32v2Val = keg::Value::basic(0, keg::BasicType::I32_V2); keg::Value i32Val = keg::Value::basic(0, keg::BasicType::I32); keg::Value stringVal = keg::Value::basic(0, keg::BasicType::STRING); keg::Value floraDirVal = keg::Value::custom(0, "FloraDir", true); // Parses slope field auto floraParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "block") { keg::evalData((ui8*)&floraProps->block, &stringVal, value, context); } else if (key == "child") { keg::evalData((ui8*)&floraProps->nextFlora, &stringVal, value, context); } else if (key == "height") { PARSE_V2(i32, floraProps->height); } else if (key == "slope") { PARSE_V2(i32, floraProps->slope); } else if (key == "dSlope") { PARSE_V2(i32, floraProps->dSlope); } else if (key == "dir") { keg::evalData((ui8*)&floraProps->dir, &floraDirVal, value, context); } }); auto baseParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { FloraKegProperties properties; properties.id = key; floraProps = &properties; context.reader.forAllInMap(value, &floraParser); genData->blockInfo.flora.push_back(properties); }); context.reader.forAllInMap(node, &baseParser); context.reader.dispose(); } void PlanetGenLoader::loadTrees(const nString& filePath, PlanetGenData* genData) { // Read in the file nString data; m_iom->readFileToString(filePath.c_str(), data); // Get the read context and error check // TODO(Ben): Too much copy paste keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to load " + filePath; context.reader.dispose(); return; } TreeKegProperties* treeProps; // Handles BranchVolumeKegProperties* branchVolProps = nullptr; TrunkKegProperties* trunkProps = nullptr; FruitKegProperties* fruitProps = nullptr; LeafKegProperties* leafProps = nullptr; // Custom values, must match PARSE_V2 syntax keg::Value i32v2Val = keg::Value::basic(0, keg::BasicType::I32_V2); keg::Value i32Val = keg::Value::basic(0, keg::BasicType::I32); keg::Value f32v2Val = keg::Value::basic(0, keg::BasicType::F32_V2); keg::Value f32Val = keg::Value::basic(0, keg::BasicType::F32); keg::Value stringVal = keg::Value::basic(0, keg::BasicType::STRING); keg::Value leafTypeVal = keg::Value::custom(0, "TreeLeafType", true); keg::Value interpTypeVal = keg::Value::custom(0, "FloraInterpType", true); /************************************************************************/ /* The following code is ugly because it must be custom parsed with */ /* PARSE_V2. It is *IMPERATIVE* that any added properties be set in */ /* SoaEngine::initVoxelGen as well. */ /************************************************************************/ // Parses fruit field auto fruitParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "id") { keg::evalData((ui8*)&fruitProps->flora, &stringVal, value, context); } else if (key == "chance") { PARSE_V2(f32, fruitProps->chance); } }); // Parses leaf field auto leafParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { // TODO(Ben): String tokenizer to get rid of string comparisons if (key == "type") { keg::evalData((ui8*)&leafProps->type, &leafTypeVal, value, context); } else if (key == "radius") { PARSE_V2(i32, leafProps->round.vRadius); leafProps->round.hRadius = leafProps->round.vRadius; } else if (key == "vRadius") { PARSE_V2(i32, leafProps->round.vRadius); } else if (key == "hRadius") { PARSE_V2(i32, leafProps->round.hRadius); } else if (key == "oRadius") { PARSE_V2(i32, leafProps->pine.oRadius); } else if (key == "iRadius") { PARSE_V2(i32, leafProps->pine.iRadius); } else if (key == "period") { PARSE_V2(i32, leafProps->pine.period); } else if (key == "block") { keg::evalData((ui8*)&leafProps->block, &stringVal, value, context); } else if (key == "fruit") { fruitProps = &leafProps->fruitProps; context.reader.forAllInMap(value, &fruitParser); } else if (key == "tvRadius") { PARSE_V2(i32, leafProps->mushroom.tvRadius); } else if (key == "thRadius") { PARSE_V2(i32, leafProps->mushroom.thRadius); } else if (key == "bvRadius") { PARSE_V2(i32, leafProps->mushroom.bvRadius); } else if (key == "bhRadius") { PARSE_V2(i32, leafProps->mushroom.bhRadius); } else if (key == "bLength") { PARSE_V2(i32, leafProps->mushroom.bLength); } else if (key == "capWidth") { PARSE_V2(i32, leafProps->mushroom.capWidth); } else if (key == "gillWidth") { PARSE_V2(i32, leafProps->mushroom.gillWidth); } else if (key == "gillBlock") { keg::evalData((ui8*)&leafProps->mushGillBlock, &stringVal, value, context); } else if (key == "capBlock") { keg::evalData((ui8*)&leafProps->mushCapBlock, &stringVal, value, context); } else if (key == "interp") { keg::evalData((ui8*)&leafProps->mushroom.interp, &interpTypeVal, value, context); } }); // Parses branch field auto branchParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "coreWidth") { PARSE_V2(i32, trunkProps->branchProps.coreWidth); } else if (key == "barkWidth") { PARSE_V2(i32, trunkProps->branchProps.barkWidth); } else if (key == "widthFalloff") { PARSE_V2(f32, trunkProps->branchProps.widthFalloff); } else if (key == "angle") { PARSE_V2(f32, trunkProps->branchProps.angle); // Transform degrees to radians trunkProps->branchProps.angle *= DEG_TO_RAD; } else if (key == "changeDirChance") { PARSE_V2(f32, trunkProps->branchProps.changeDirChance); } else if (key == "subBranchAngle") { PARSE_V2(f32, trunkProps->branchProps.subBranchAngle); // Transform degrees to radians trunkProps->branchProps.subBranchAngle *= DEG_TO_RAD; } else if (key == "branchChance" || key == "subBranchChance") { PARSE_V2(f32, trunkProps->branchProps.branchChance); } else if (key == "block") { keg::evalData((ui8*)&trunkProps->branchProps.coreBlock, &stringVal, value, context); trunkProps->branchProps.barkBlock = trunkProps->branchProps.coreBlock; } else if (key == "coreBlock") { keg::evalData((ui8*)&trunkProps->branchProps.coreBlock, &stringVal, value, context); } else if (key == "barkBlock") { keg::evalData((ui8*)&trunkProps->branchProps.barkBlock, &stringVal, value, context); } else if (key == "fruit") { fruitProps = &trunkProps->branchProps.fruitProps; context.reader.forAllInMap(value, &fruitParser); } else if (key == "leaves") { leafProps = &trunkProps->branchProps.leafProps; context.reader.forAllInMap(value, &leafParser); } }); // Parses slope field auto slopeParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "min") { PARSE_V2(i32, trunkProps->slope[0]); } else if (key == "max") { PARSE_V2(i32, trunkProps->slope[1]); } }); // Parses fourth level auto trunkDataParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "loc") { keg::evalData((ui8*)&trunkProps->loc, &f32Val, value, context); } else if (key == "coreWidth") { PARSE_V2(i32, trunkProps->coreWidth); } else if (key == "barkWidth") { PARSE_V2(i32, trunkProps->barkWidth); } else if (key == "branchChance") { PARSE_V2(f32, trunkProps->branchChance); } else if (key == "changeDirChance") { PARSE_V2(f32, trunkProps->changeDirChance); } else if (key == "slope") { context.reader.forAllInMap(value, &slopeParser); } else if (key == "block") { keg::evalData((ui8*)&trunkProps->coreBlock, &stringVal, value, context); trunkProps->barkBlock = trunkProps->coreBlock; } else if (key == "coreBlock") { keg::evalData((ui8*)&trunkProps->coreBlock, &stringVal, value, context); } else if (key == "barkBlock") { keg::evalData((ui8*)&trunkProps->barkBlock, &stringVal, value, context); } else if (key == "fruit") { fruitProps = &trunkProps->fruitProps; context.reader.forAllInMap(value, &fruitParser); } else if (key == "branches") { context.reader.forAllInMap(value, &branchParser); } else if (key == "leaves") { leafProps = &trunkProps->leafProps; context.reader.forAllInMap(value, &leafParser); } else if (key == "interp") { keg::evalData((ui8*)&trunkProps->interp, &interpTypeVal, value, context); } }); // Parses third level auto trunkParser = makeFunctor([&](Sender, size_t size VORB_MAYBE_UNUSED, keg::Node value) { treeProps->trunkProps.emplace_back(); // Get our handle trunkProps = &treeProps->trunkProps.back(); *trunkProps = {}; // Zero it // Default slopes trunkProps->slope[0] = i32v2(1000); trunkProps->slope[1] = i32v2(1000); context.reader.forAllInMap(value, &trunkDataParser); // Avoid div 0 if (trunkProps->slope[0].x < 1) trunkProps->slope[0].x = 1; if (trunkProps->slope[0].y < 1) trunkProps->slope[0].y = 1; if (trunkProps->slope[1].x < 1) trunkProps->slope[1].x = 1; if (trunkProps->slope[1].y < 1) trunkProps->slope[1].y = 1; }); // Parses a branch volume auto branchVolumeParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "height") { PARSE_V2(i32, branchVolProps->height); } else if (key == "hRadius") { PARSE_V2(i32, branchVolProps->hRadius); } else if (key == "vRadius") { PARSE_V2(i32, branchVolProps->vRadius); } else if (key == "radius") { PARSE_V2(i32, branchVolProps->hRadius); branchVolProps->vRadius = branchVolProps->hRadius; } else if (key == "points") { PARSE_V2(i32, branchVolProps->points); } }); // Parses array of branch volumes auto branchVolumeSeqParser = makeFunctor([&](Sender, size_t size VORB_MAYBE_UNUSED, keg::Node value) { treeProps->branchVolumes.emplace_back(); // Get our handle branchVolProps = &treeProps->branchVolumes.back(); *branchVolProps = {}; // Zero it context.reader.forAllInMap(value, &branchVolumeParser); }); // Parses second level auto treeParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { if (key == "height") { PARSE_V2(i32, treeProps->height); } else if (key == "branchPoints") { PARSE_V2(i32, treeProps->branchPoints); } else if (key == "branchStep") { PARSE_V2(i32, treeProps->branchStep); } else if (key == "killMult") { PARSE_V2(i32, treeProps->killMult); } else if (key == "infRadius") { PARSE_V2(f32, treeProps->infRadius); } else if (key == "branchVolumes") { context.reader.forAllInSequence(value, &branchVolumeSeqParser); } else if (key == "trunk") { context.reader.forAllInSequence(value, &trunkParser); } }); // Parses top level auto baseParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { genData->blockInfo.trees.emplace_back(); treeProps = &genData->blockInfo.trees.back(); *treeProps = {}; // Zero it treeProps->id = key; context.reader.forAllInMap(value, &treeParser); }); context.reader.forAllInMap(node, &baseParser); context.reader.dispose(); } #undef PARSE_V2 void PlanetGenLoader::loadBiomes(const nString& filePath, PlanetGenData* genData) { // Read in the file nString data; m_iom->readFileToString(filePath.c_str(), data); // Get the read context and error check keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to load " + filePath; context.reader.dispose(); return; } // Lookup Maps std::map m_baseBiomeLookupMap; ///< To lookup biomes via color code BiomeColorCode colorCodes[BIOME_MAP_WIDTH][BIOME_MAP_WIDTH]; std::vector baseBiomes; // Load yaml data // int i = 0; auto baseParser = makeFunctor([&](Sender, const nString& key, keg::Node value) { // Parse based on type if (key == "baseLookupMap") { vpath texPath; m_iom->resolvePath(keg::convert(value), texPath); vg::ScopedBitmapResource rs(vg::ImageIO().load(texPath.getString(), vg::ImageIOFormat::RGB_UI8, true)); if (!rs.data) { pError("Failed to load " + keg::convert(value)); } if (rs.width != BIOME_MAP_WIDTH || rs.height != BIOME_MAP_WIDTH) { pError("loadBiomes() error: width and height of " + keg::convert(value) +" must be " + std::to_string(BIOME_MAP_WIDTH)); } for (int i = 0; i < BIOME_MAP_WIDTH * BIOME_MAP_WIDTH; i++) { ui8v3& color = rs.bytesUI8v3[i]; BiomeColorCode colorCode = ((ui32)color.r << 16) | ((ui32)color.g << 8) | (ui32)color.b; colorCodes[i / BIOME_MAP_WIDTH][i % BIOME_MAP_WIDTH] = colorCode; } } else { // It is a base biome baseBiomes.emplace_back(); BiomeKegProperties& props = baseBiomes.back(); props.id = key; // Parse it keg::Error error = keg::parse((ui8*)&props, value, context, &KEG_GLOBAL_TYPE(BiomeKegProperties)); if (error != keg::Error::NONE) { fprintf(stderr, "Keg error %d in loadBiomes()\n", (int)error); return; } } }); context.reader.forAllInMap(node, &baseParser); context.reader.dispose(); // Get number of biomes ui32 numBiomes = 0; for (auto& kp : baseBiomes) { numBiomes += recursiveCountBiomes(kp); } // Set biome storage genData->biomes.resize(numBiomes); ui32 biomeCounter = 0; for (size_t i = 0; i < baseBiomes.size(); i++) { auto& kp = baseBiomes[i]; // Get the biome Biome& biome = genData->biomes[biomeCounter++]; // Get the color code and add to map BiomeColorCode colorCode = ((ui32)kp.mapColor.r << 16) | ((ui32)kp.mapColor.g << 8) | (ui32)kp.mapColor.b; m_baseBiomeLookupMap[colorCode] = &biome; // Copy all the data over recursiveInitBiomes(biome, kp, biomeCounter, genData); } assert(biomeCounter == genData->biomes.size()); // Set base biomes memset(genData->baseBiomeLookup, 0, sizeof(genData->baseBiomeLookup)); for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { auto it = m_baseBiomeLookupMap.find(colorCodes[y][x]); if (it != m_baseBiomeLookupMap.end()) { genData->baseBiomeLookup[y][x] = it->second; } } } // Blur base biome map for transition smoothing std::vector> outMap; blurBaseBiomeMap(genData->baseBiomeLookup, outMap); // Convert to influence map for (int y = 0; y < BIOME_MAP_WIDTH; y++) { for (int x = 0; x < BIOME_MAP_WIDTH; x++) { genData->baseBiomeInfluenceMap[y][x].resize(outMap[y * BIOME_MAP_WIDTH + x].size()); int i = 0; for (auto& b : outMap[y * BIOME_MAP_WIDTH + x]) { genData->baseBiomeInfluenceMap[y][x][i++] = b; } } } } void PlanetGenLoader::parseTerrainFuncs(NoiseBase* terrainFuncs, keg::ReadContext& context, keg::Node node) { if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to parse node"; return; } keg::Error error = keg::parse((ui8*)terrainFuncs, node, context, &KEG_GLOBAL_TYPE(NoiseBase)); if (error != keg::Error::NONE) { fprintf(stderr, "Keg error %d in parseTerrainFuncs()\n", (int)error); return; } } void PlanetGenLoader::parseLiquidColor(keg::ReadContext& context, keg::Node node, PlanetGenData* genData) { if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to parse node"; return; } LiquidColorKegProperties kegProps; keg::Error error; error = keg::parse((ui8*)&kegProps, node, context, &KEG_GLOBAL_TYPE(LiquidColorKegProperties)); if (error != keg::Error::NONE) { fprintf(stderr, "Keg error %d in parseLiquidColor()\n", (int)error); return; } // TODO: Reimplement these as suitable. if (kegProps.colorPath.size()) { if (m_glRpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender, void*) { //m_textureCache.freeTexture(kegProps.colorPath); //genData->liquidColorMap = m_textureCache.addTexture(kegProps.colorPath, // genData->liquidColorPixels, // vg::ImageIOFormat::RGB_UI8, // vg::TextureTarget::TEXTURE_2D, // &vg::SamplerState::LINEAR_CLAMP, // vg::TextureInternalFormat::RGB8, // vg::TextureFormat::RGB, true); })); m_glRpc->invoke(&rpc, true); delete rpc.data.f; } else { //m_textureCache.freeTexture(kegProps.colorPath); //genData->liquidColorMap = m_textureCache.addTexture(kegProps.colorPath, // genData->liquidColorPixels, // vg::ImageIOFormat::RGB_UI8, // vg::TextureTarget::TEXTURE_2D, // &vg::SamplerState::LINEAR_CLAMP, // vg::TextureInternalFormat::RGB8, // vg::TextureFormat::RGB, true); } // Turn into a color map if (genData->liquidColorMap.id == 0) { vg::ImageIO::free(genData->liquidColorPixels); } } if (kegProps.texturePath.size()) { // TODO: Reimplement these as suitable. // Handle RPC for texture upload if (m_glRpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender s VORB_UNUSED, void* userData VORB_UNUSED) { //genData->liquidTexture = m_textureCache.addTexture(kegProps.texturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); })); m_glRpc->invoke(&rpc, true); delete rpc.data.f; } else { //genData->liquidTexture = m_textureCache.addTexture(kegProps.texturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); } } genData->liquidFreezeTemp = kegProps.freezeTemp; genData->liquidDepthScale = kegProps.depthScale; genData->liquidTint = kegProps.tint; } void PlanetGenLoader::parseTerrainColor(keg::ReadContext& context VORB_MAYBE_UNUSED, keg::Node node, PlanetGenData* genData VORB_MAYBE_UNUSED) { if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to parse node"; return; } TerrainColorKegProperties kegProps; keg::Error error; error = keg::parse((ui8*)&kegProps, node, context, &KEG_GLOBAL_TYPE(TerrainColorKegProperties)); if (error != keg::Error::NONE) { fprintf(stderr, "Keg error %d in parseTerrainColor()\n", (int)error); return; } if (kegProps.colorPath.size()) { vio::Path p; if (m_iom->resolvePath(kegProps.colorPath, p)) { // Handle RPC for texture upload genData->terrainColorPixels = vg::ImageIO().load(p, vg::ImageIOFormat::RGB_UI8, true); } } // TODO(Ben): stop being lazy and copy pasting if (kegProps.grassTexturePath.size()) { // Handle RPC for texture upload if (m_glRpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender s VORB_UNUSED, void* userData VORB_UNUSED) { //genData->grassTexture = m_textureCache.addTexture(kegProps.grassTexturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); })); m_glRpc->invoke(&rpc, true); delete rpc.data.f; } else { //genData->grassTexture = m_textureCache.addTexture(kegProps.grassTexturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); } } if (kegProps.rockTexturePath.size()) { // Handle RPC for texture upload if (m_glRpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender s VORB_UNUSED, void* userData VORB_UNUSED) { //genData->rockTexture = m_textureCache.addTexture(kegProps.rockTexturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); })); m_glRpc->invoke(&rpc, true); delete rpc.data.f; } else { //genData->rockTexture = m_textureCache.addTexture(kegProps.rockTexturePath, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); } } genData->terrainTint = kegProps.tint; } void PlanetGenLoader::parseBlockLayers(keg::ReadContext& context, keg::Node node, PlanetGenData* genData) { if (keg::getType(node) != keg::NodeType::MAP) { std::cout << "Failed to parse node in parseBlockLayers. Should be MAP"; return; } auto f = makeFunctor([&](Sender, const nString& name, keg::Node value) { BlockLayerKegProperties layerProps = {}; layerProps.block = name; keg::parse((ui8*)&layerProps, value, context, &KEG_GLOBAL_TYPE(BlockLayerKegProperties)); genData->blockInfo.blockLayers.push_back(layerProps); }); context.reader.forAllInMap(node, &f); } ================================================ FILE: SoA/PlanetGenLoader.h ================================================ /// /// PlanetLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 19 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles the loading of planet files /// #pragma once #ifndef PlanetLoader_h__ #define PlanetLoader_h__ #pragma once #include #include #include #include #include "PlanetGenerator.h" #include "SpaceSystemLoadStructs.h" DECL_VIO(class IOManager); DECL_VCORE(class RPCManager); struct NoiseBase; struct PlanetGenData; struct BiomeKegProperties; class BlockPack; typedef ui32 BiomeColorCode; class PlanetGenLoader { public: void init(vio::IOManager* ioManager); /// Loads a planet from file CALLER_DELETE PlanetGenData* loadPlanetGenData(const nString& terrainPath); /// Returns a default planetGenData /// @param glrpc: Optional RPC if you want to load on a non-render thread /// @return planet gen data PlanetGenData* getDefaultGenData(vcore::RPCManager* glrpc = nullptr); /// Returns a default planetGenData /// @param glrpc: Optional RPC if you want to load on a non-render thread /// @return planet gen data CALLER_DELETE PlanetGenData* getRandomGenData(f32 radius, vcore::RPCManager* glrpc = nullptr); AtmosphereProperties getRandomAtmosphere(); private: void loadFlora(const nString& filePath, PlanetGenData* genData); void loadTrees(const nString& filePath, PlanetGenData* genData); void loadBiomes(const nString& filePath, PlanetGenData* genData); void parseTerrainFuncs(NoiseBase* terrainFuncs, keg::ReadContext& context, keg::Node node); void parseLiquidColor(keg::ReadContext& context, keg::Node node, PlanetGenData* genData); void parseTerrainColor(keg::ReadContext& context, keg::Node node, PlanetGenData* genData); void parseBlockLayers(keg::ReadContext& context, keg::Node node, PlanetGenData* genData); static PlanetGenData* m_defaultGenData; ///< Default generation data handle vio::IOManager* m_iom = nullptr; ///< IOManager handle vg::TextureCache m_textureCache; ///< Texture cache for re-using textures vcore::RPCManager* m_glRpc = nullptr; PlanetGenerator m_planetGenerator; }; #endif // PlanetLoader_h__ ================================================ FILE: SoA/PlanetGenerator.cpp ================================================ #include "stdafx.h" #include "PlanetGenerator.h" #include "ShaderLoader.h" #include "SoaOptions.h" #include #include #define BLUR_PASSES 4 PlanetGenerator::PlanetGenerator() { // Empty } void PlanetGenerator::dispose(vcore::RPCManager* glrpc VORB_UNUSED) { // TODO(Ben): Implement } CALLER_DELETE PlanetGenData* PlanetGenerator::generateRandomPlanet(SpaceObjectType type, vcore::RPCManager* glrpc /* = nullptr */) { switch (type) { case SpaceObjectType::PLANET: case SpaceObjectType::DWARF_PLANET: case SpaceObjectType::MOON: case SpaceObjectType::DWARF_MOON: return generatePlanet(glrpc); case SpaceObjectType::ASTEROID: return generateAsteroid(glrpc); case SpaceObjectType::COMET: return generateComet(glrpc); default: return nullptr; } } CALLER_DELETE PlanetGenData* PlanetGenerator::generatePlanet(vcore::RPCManager* glrpc) { PlanetGenData* data = new PlanetGenData; data->terrainColorMap = getRandomColorMap(glrpc, true); data->liquidColorMap = getRandomColorMap(glrpc, true); // Falloffs static std::uniform_real_distribution falloff(0.0f, 100.0f); f32 f = falloff(m_generator); data->tempLatitudeFalloff = f * 1.9f; data->tempHeightFalloff = 5.0f; data->humLatitudeFalloff = f * 0.3f; data->humHeightFalloff = 1.0f; std::vector funcs; // Mountains getRandomTerrainFuncs(funcs, TerrainStage::RIDGED_NOISE, 0, 2, 3, 7, 0.00001f, 0.001f, -15000.0f, 15000.0f, 100.0f, 30000.0f); // Terrain getRandomTerrainFuncs(funcs, TerrainStage::NOISE, 2, 5, 1, 4, 0.0002f, 0.2f, -500.0f, 500.0f, 10.0f, 1000.0f); data->baseTerrainFuncs.funcs.setData(funcs.data(), funcs.size()); funcs.clear(); // Temperature data->tempTerrainFuncs.base = 128.0f; getRandomTerrainFuncs(funcs, TerrainStage::NOISE, 1, 2, 3, 8, 0.00008f, 0.008f, -128.0f, -128.0f, 255.0f, 255.0f); data->tempTerrainFuncs.funcs.setData(funcs.data(), funcs.size()); funcs.clear(); // Humidity data->humTerrainFuncs.base = 128.0f; getRandomTerrainFuncs(funcs, TerrainStage::NOISE, 1, 2, 3, 8, 0.00008f, 0.008f, -128.0f, -128.0f, 255.0f, 255.0f); data->humTerrainFuncs.funcs.setData(funcs.data(), funcs.size()); funcs.clear(); return data; } CALLER_DELETE PlanetGenData* PlanetGenerator::generateAsteroid(vcore::RPCManager* glrpc VORB_MAYBE_UNUSED) { PlanetGenData* data = new PlanetGenData; return data; } CALLER_DELETE PlanetGenData* PlanetGenerator::generateComet(vcore::RPCManager* glrpc VORB_MAYBE_UNUSED) { PlanetGenData* data = new PlanetGenData; return data; } VGTexture PlanetGenerator::getRandomColorMap(vcore::RPCManager* glrpc, bool shouldBlur) { static const int WIDTH = 256; color4 pixels[WIDTH][WIDTH]; static std::uniform_int_distribution numColors(4, 12); static std::uniform_int_distribution rPos(0, WIDTH - 1); static std::uniform_int_distribution rColor(0, 16777215); // 0-2^24 int numPoints = numColors(m_generator); std::vector randColors(numPoints); std::vector randPoints(numPoints); for (int i = 0; i < numPoints; i++) { int newColor = rColor(m_generator); randColors[i].r = (ui8)(newColor >> 16); randColors[i].g = (ui8)((newColor >> 8) & 0xFF); randColors[i].b = (ui8)(newColor & 0xFF); randColors[i].a = (ui8)255; randPoints[i].x = rPos(m_generator); randPoints[i].y = rPos(m_generator); } // Voronoi diagram generation // TODO(Ben): n^3 is slow for (int y = 0; y < WIDTH; y++) { for (int x = 0; x < WIDTH; x++) { int closestDist = INT_MAX; int closestIndex = 0; for (int i = 0; i < numPoints; i++) { int dx = (x - randPoints[i].x); int dy = (y - randPoints[i].y); int dist = dx * dx + dy * dy; if (dist < closestDist) { closestDist = dist; closestIndex = i; } } pixels[y][x] = randColors[closestIndex]; } } // TODO: Implement these as suitable. // Upload texture VGTexture tex = 0; if (glrpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(makeFunctor([&](Sender, void*) { tex = vg::GpuMemory::uploadTexture(pixels, WIDTH, WIDTH, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP); })); glrpc->invoke(&rpc, true); delete rpc.data.f; } else { tex = vg::GpuMemory::uploadTexture(pixels, WIDTH, WIDTH, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP); } // Handle Gaussian blur auto f = makeFunctor([&](Sender, void*) { if (!m_blurPrograms[0].isCreated()) { m_blurPrograms[0] = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/Blur.frag", nullptr, "#define HORIZONTAL_BLUR_9\n"); m_blurPrograms[1] = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/Blur.frag", nullptr, "#define VERTICAL_BLUR_9\n"); m_quad.init(); m_targets[0].setSize(WIDTH, WIDTH); m_targets[1].setSize(WIDTH, WIDTH); m_targets[0].init(); m_targets[1].init(); } // Bind the voronoi color map glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); // Compute BLUR_PASSES passes of 2-pass gaussian blur for (int p = 0; p < BLUR_PASSES; p++) { // Two pass Gaussian blur for (int i = 0; i < 2; i++) { m_blurPrograms[i].use(); m_blurPrograms[i].enableVertexAttribArrays(); glUniform1i(m_blurPrograms[i].getUniform("unTex"), 0); glUniform1f(m_blurPrograms[i].getUniform("unSigma"), 5.0f); glUniform1f(m_blurPrograms[i].getUniform("unBlurSize"), 1.0f / (f32)WIDTH); m_targets[i].use(); m_quad.draw(); m_targets[i].unuse((ui32)soaOptions.get(OPT_SCREEN_WIDTH).value.i, (ui32)soaOptions.get(OPT_SCREEN_HEIGHT).value.i); m_blurPrograms[i].disableVertexAttribArrays(); m_blurPrograms[i].unuse(); m_targets[i].bindTexture(); } } // Get the pixels and use them to re-upload the texture // TODO(Ben): A direct copy would be more efficient. glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, WIDTH, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); }); // See if we should use RPC if (shouldBlur) { if (glrpc) { vcore::RPC rpc; rpc.data.f = new vcore::RPCFunction(std::move(f)); glrpc->invoke(&rpc, true); delete rpc.data.f; } else { f.invoke(nullptr, nullptr); } } glBindTexture(GL_TEXTURE_2D, 0); return tex; } void PlanetGenerator::getRandomTerrainFuncs(OUT std::vector& funcs, TerrainStage func, int funcsRange1, int funcsRange2, int octavesRange1, int octavesRange2, float freqRange1, float freqRange2, float heightMinRange1, float heightMinRange2, float heightWidthRange1, float heightWidthRange2) { std::uniform_int_distribution funcsRange(funcsRange1, funcsRange2); std::uniform_int_distribution octavesRange(octavesRange1, octavesRange2); std::uniform_real_distribution freqRange(freqRange1, freqRange2); std::uniform_real_distribution heightMinRange(heightMinRange1, heightMinRange2); std::uniform_real_distribution heightWidthRange(heightWidthRange1, heightWidthRange2); int numFuncs = funcsRange(m_generator); if (numFuncs <= 0) return; funcs.resize(funcs.size() + numFuncs); for (int i = 0; i < numFuncs; i++) { auto& f = funcs[i]; f.func = func; f.low = heightMinRange(m_generator); f.high = f.low + heightWidthRange(m_generator); f.octaves = octavesRange(m_generator); f.frequency = freqRange(m_generator); f.persistence = 0.8f; } } ================================================ FILE: SoA/PlanetGenerator.h ================================================ /// /// PlanetGenerator.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 16 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Generates random planet data /// #pragma once #ifndef PlanetGenerator_h__ #define PlanetGenerator_h__ #include "PlanetGenData.h" #include "SpaceSystemLoadStructs.h" #include #include #include #include #include #include #include #include struct PlanetGenData; class PlanetGenerator { public: PlanetGenerator(); void dispose(vcore::RPCManager* glrpc); CALLER_DELETE PlanetGenData* generateRandomPlanet(SpaceObjectType type, vcore::RPCManager* glrpc = nullptr); private: CALLER_DELETE PlanetGenData* generatePlanet(vcore::RPCManager* glrpc); CALLER_DELETE PlanetGenData* generateAsteroid(vcore::RPCManager* glrpc); CALLER_DELETE PlanetGenData* generateComet(vcore::RPCManager* glrpc); VGTexture getRandomColorMap(vcore::RPCManager* glrpc, bool shouldBlur); void getRandomTerrainFuncs(OUT std::vector& funcs, TerrainStage func, int funcsRange1, int funcsRange2, int octavesRange1, int octavesRange2, float freqRange1, float freqRange2, float heightMinRange1, float heightMinRange2, float heightWidthRange1, float heightWidthRange2); vg::FullQuadVBO m_quad; vg::GLRenderTarget m_targets[2]; vg::GLProgram m_blurPrograms[2]; std::mt19937 m_generator = std::mt19937(36526); }; #endif // PlanetGenerator_h__ ================================================ FILE: SoA/PlanetHeightData.h ================================================ /// /// PlanetHeightData.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 9 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Heightmap data for planets /// #pragma once #ifndef PlanetHeightData_h__ #define PlanetHeightData_h__ struct Biome; struct PlanetHeightData { const Biome* biome; f32 height; ///< Height in voxels ui16 flora; ui8 temperature; ui8 humidity; ui8 flags; // TODO(Ben): Bitfield }; #endif // PlanetHeightData_h__ ================================================ FILE: SoA/PlanetRenderStage.cpp ================================================ #include "stdafx.h" #include "PlanetRenderStage.h" #include #include #include "Chunk.h" #include "ChunkManager.h" #include "ChunkIOManager.h" #include "DebugRenderer.h" #include "GLProgramManager.h" #include "GameManager.h" #include "MeshManager.h" #include "Options.h" #include "Player.h" #include "Planet.h" #include "SkyboxRenderer.h" #include "Texture2d.h" #include "VoxelEditor.h" #include "VoxelWorld.h" PlanetRenderStage::PlanetRenderStage(const Camera* camera) : IRenderStage(camera) { // Empty } void PlanetRenderStage::draw() { f32m4 VP = _camera->getProjectionMatrix() * _camera->getViewMatrix(); DepthState::FULL.set(); glBlendFunc(GL_ONE, GL_ZERO); GameManager::planet->draw(0, _camera, f32v3(1.0f, 0.0f, 0.0f), 0.1 /*_ambientLight + 0.1*/, _camera->getNearClip() / planetScale, true /*connectedToPlanet*/); DepthState::READ.set(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); RasterizerState::CULL_CLOCKWISE.set(); if (true /*connectedToPlanet*/) { if (!drawMode) GameManager::planet->atmosphere.draw((float)0, VP, glm::vec3((GameManager::planet->invRotationMatrix) * glm::vec4(1.0f, 0.0f, 0.0f, 1.0)), _camera->getPosition()); } else { if (!drawMode) GameManager::planet->atmosphere.draw((float)0, VP, f32v3(1.0f, 0.0f, 0.0f), _camera->getPosition()); } DepthState::FULL.set(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Chunk::vboIndicesID); GameManager::planet->drawTrees(VP, _camera->getPosition(), 0.1f /*ambVal*/); } ================================================ FILE: SoA/PlanetRenderStage.h ================================================ /// /// PlanetRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the planet rendering stage, which renders /// planets and their atmospheres. /// #pragma once #ifndef PlanetRenderStage_h__ #define PlanetRenderStage_h__ #include class Camera; class PlanetRenderStage : public vg::IRenderStage { public: /// Constructor which injects dependencies /// @param camera: The camera handle PlanetRenderStage(const Camera* camera); /// Draws the render stage virtual void draw() override; }; #endif // PlanetRenderStage_h__ ================================================ FILE: SoA/PlanetRingsComponentRenderer.cpp ================================================ #include "stdafx.h" #include "PlanetRingsComponentRenderer.h" #include "SpaceSystem.h" #include "RenderUtils.h" #include "soaUtils.h" #include "ShaderLoader.h" #include #include #include #include PlanetRingsComponentRenderer::~PlanetRingsComponentRenderer() { dispose(); } void PlanetRingsComponentRenderer::initGL() { if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/PlanetRings/Rings.vert", "Shaders/PlanetRings/Rings.frag"); } if (!m_isInitialized) { m_isInitialized = true; m_quad.init(); } } void PlanetRingsComponentRenderer::draw(const PlanetRingsComponent& prCmp, vecs::EntityID eid, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightPos, const f32 planetRadius, const f32 zCoef, const SpaceLightComponent* spComponent VORB_UNUSED) { // Get renderables // TODO(Ben): Use a renderable component instead std::vector* rings; auto it = m_renderableRings.find(eid); if (it == m_renderableRings.end()) { // Look how ugly this line is. rings = &m_renderableRings.insert(std::make_pair(eid, std::vector(prCmp.rings.size()))).first->second; for (size_t i = 0; i < prCmp.rings.size(); i++) { auto& rr = rings->operator[](i); rr.ring = prCmp.rings[i]; // Load the texture vg::ScopedBitmapResource b(vg::ImageIO().load(rr.ring.texturePath)); if (b.data) { rr.texture = vg::GpuMemory::uploadTexture(&b, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP); } else { fprintf(stderr, "Failed to load %s\n", rr.ring.texturePath.getCString()); } } } else { rings = &it->second; } m_program.use(); // For logarithmic Z buffer glUniform1f(m_program.getUniform("unZCoef"), zCoef); glDisable(GL_CULL_FACE); // Set up matrix for (auto& r : (*rings)) { // TODO(Matthew): This statement wasn't used, revisit this function to make sure it is behaving correctly. //f64q invOrientation = glm::inverse(r.ring.orientation); // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)r.ring.orientation.x; orientationF32.y = (f32)r.ring.orientation.y; orientationF32.z = (f32)r.ring.orientation.z; orientationF32.w = (f32)r.ring.orientation.w; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); f32m4 W(1.0); setMatrixScale(W, f32v3(r.ring.outerRadius)); setMatrixTranslation(W, -relCamPos); W *= rotationMatrix; f32m4 WVP = VP * W; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, r.texture); glUniformMatrix4fv(m_program.getUniform("unM"), 1, GL_FALSE, &W[0][0]); glUniformMatrix4fv(m_program.getUniform("unMVP"), 1, GL_FALSE, &WVP[0][0]); glUniform1f(m_program.getUniform("unInnerRadius"), r.ring.innerRadius); glUniform1f(m_program.getUniform("unOuterRadius"), r.ring.outerRadius); glUniform1i(m_program.getUniform("unColorLookup"), 0); glUniform3fv(m_program.getUniform("unLightPos"), 1, &lightPos[0]); f32v3 planetPos = -relCamPos; glUniform3fv(m_program.getUniform("unPlanetPos"), 1, &planetPos[0]); glUniform1f(m_program.getUniform("unPlanetRadius"), planetRadius); m_quad.draw(); } glEnable(GL_CULL_FACE); m_program.unuse(); } void PlanetRingsComponentRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); if (m_isInitialized) { m_quad.dispose(); m_isInitialized = false; } } ================================================ FILE: SoA/PlanetRingsComponentRenderer.h ================================================ /// /// PlanetRingsComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 23 May 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// The renderer for PlanetRingsComponent /// #pragma once #ifndef PlanetRingsComponentRenderer_h__ #define PlanetRingsComponentRenderer_h__ #include #include #include #include #include #include #include "SpaceSystemComponents.h" struct SpaceLightComponent; struct PlanetRingsComponent; // TODO(Ben): Use a renderable component instead struct RenderableRing { PlanetRing ring; VGTexture texture; }; class PlanetRingsComponentRenderer { public: ~PlanetRingsComponentRenderer(); void initGL(); void draw(const PlanetRingsComponent& prCmp, vecs::EntityID eid, const f32m4& VP, const f32v3& relCamPos, const f32v3& lightPos, const f32 planetRadius, const f32 zCoef, const SpaceLightComponent* spComponent); void dispose(); private: // TODO(Ben): Use a renderable component instead std::unordered_map> m_renderableRings; vg::GLProgram m_program; vg::FullQuadVBO m_quad; bool m_isInitialized = false; }; #endif // PlanetRingsComponentRenderer_h__ ================================================ FILE: SoA/Positional.h ================================================ // // Positional.h // Seed of Andromeda // // Created by Cristian Zaloj on 28 May 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef Positional_h__ #define Positional_h__ // TODO(Cristian): Some constants don't belong here #include "Constants.h" // TODO(Cristian): Move this to Vorb types.h #define soffsetof(T, M) static_cast((ptrdiff_t)&(((T*)nullptr)->M)) typedef i32 VoxelPositionType; typedef glm::tvec3 VoxelVectorType; inline VoxelPositionType wrapInBounds(VoxelPositionType v) { return v & (CHUNK_WIDTH - 1); } class VoxelIterablePositionRawX; class VoxelIterablePositionRawY; class VoxelIterablePositionRawZ; template class VoxelIterablePositionWrapX; template class VoxelIterablePositionWrapY; template class VoxelIterablePositionWrapZ; template class VoxelIterablePositionClampX; template class VoxelIterablePositionClampY; template class VoxelIterablePositionClampZ; struct VoxelIterablePosition { public: VoxelPositionType x, y, z; VoxelIterablePositionRawX& rx() { return reinterpret_cast(*this); } VoxelIterablePositionRawY& ry() { return reinterpret_cast(*this); } VoxelIterablePositionRawZ& rz() { return reinterpret_cast(*this); } template VoxelIterablePositionWrapX& wx() { return reinterpret_cast&>(*this); } template VoxelIterablePositionWrapY& wy() { return reinterpret_cast&>(*this); } template VoxelIterablePositionWrapZ& wz() { return reinterpret_cast&>(*this); } template VoxelIterablePositionClampX& cx() { return reinterpret_cast&>(*this); } template VoxelIterablePositionClampY& cy() { return reinterpret_cast&>(*this); } template VoxelIterablePositionClampZ& cz() { return reinterpret_cast&>(*this); } operator VoxelVectorType() const { return VoxelVectorType(x, y, z); } }; // Check alignment against glm types #ifdef _MSC_VER //re-interpret cast cannot be used in constexpr static_assert(soffsetof(VoxelIterablePosition, x) == soffsetof(VoxelVectorType, x), "VoxelIterablePosition X is misaligned"); static_assert(soffsetof(VoxelIterablePosition, y) == soffsetof(VoxelVectorType, y), "VoxelIterablePosition Y is misaligned"); static_assert(soffsetof(VoxelIterablePosition, z) == soffsetof(VoxelVectorType, z), "VoxelIterablePosition Z is misaligned"); #endif// _MSC_VER static_assert(sizeof(VoxelIterablePosition) == sizeof(VoxelVectorType), "VoxelIterablePosition is of wrong size"); /*! @brief This operator class modifies value without any modifications. * * This class only exists to allow for templated code to take advantage of different iterator forms. *
* pos.x += 5 performs the same modifications as pos.rx() += 5; *
* although (pos.rx() += 5).ry() += 5 is a valid statement */ template struct VoxelIterablePositionRaw : public VoxelIterablePosition { public: VoxelIterablePositionRaw& operator+= (VoxelPositionType v) { this->*M += v; return *this; } VoxelIterablePositionRaw operator+ (VoxelPositionType v) { VoxelIterablePositionRaw pos(*this); pos += v; return pos; } // operator VoxelIterablePosition() const { // return *this; // } }; class VoxelIterablePositionRawX : public VoxelIterablePositionRaw<&VoxelIterablePosition::x> {}; class VoxelIterablePositionRawY : public VoxelIterablePositionRaw<&VoxelIterablePosition::y> {}; class VoxelIterablePositionRawZ : public VoxelIterablePositionRaw<&VoxelIterablePosition::z> {}; /*! @brief This operator class wraps values at the end of a modification between [0, (1 << BITS) - 1] */ template struct VoxelIterablePositionWrap : public VoxelIterablePosition { public: static const VoxelPositionType BIT_MASK = (1 << BITS) - 1; void wrap() { this->*M &= BIT_MASK; } VoxelIterablePositionWrap& operator+= (VoxelPositionType v) { this->*M += v; this->*M &= BIT_MASK; return *this; } VoxelIterablePositionWrap operator+ (VoxelPositionType v) { VoxelIterablePositionWrap pos(*this); pos += v; return pos; } // operator VoxelIterablePosition() const { // return *this; // } }; template class VoxelIterablePositionWrapX : public VoxelIterablePositionWrap {}; template class VoxelIterablePositionWrapY : public VoxelIterablePositionWrap {}; template class VoxelIterablePositionWrapZ : public VoxelIterablePositionWrap {}; /*! @brief This operator class clamps values at the end of a modification between [MIN, MAX] */ template struct VoxelIterablePositionClamp : public VoxelIterablePosition { public: void clamp() { if (this->*M > MAX) this->*M = MAX; else if (this->*M < MIN) this->*M = MIN; } VoxelIterablePositionClamp& operator+= (VoxelPositionType v) { this->*M += v; if (this->*M > MAX) this->*M = MAX; else if (this->*M < MIN) this->*M = MIN; return *this; } VoxelIterablePositionClamp operator+ (VoxelPositionType v) { VoxelIterablePositionClamp pos(*this); pos += v; return pos; } // operator VoxelIterablePosition() const { // return *this; // } }; template class VoxelIterablePositionClampX : public VoxelIterablePositionClamp {}; template class VoxelIterablePositionClampY : public VoxelIterablePositionClamp {}; template class VoxelIterablePositionClampZ : public VoxelIterablePositionClamp {}; struct VoxelPosition { public: VoxelIterablePosition chunk; VoxelIterablePosition voxel; }; #endif // Positional_h__ ================================================ FILE: SoA/ProceduralChunkGenerator.cpp ================================================ #include "stdafx.h" #include "ProceduralChunkGenerator.h" #include "Chunk.h" #include "Constants.h" #include "VoxelSpaceConversions.h" #include "SmartVoxelContainer.hpp" void ProceduralChunkGenerator::init(PlanetGenData* genData) { m_genData = genData; m_heightGenerator.init(genData); } void ProceduralChunkGenerator::generateChunk(Chunk* chunk, PlanetHeightData* heightData) const { //int temperature; //int rainfall; int height; int mapHeight; int hIndex; int depth; ui16 blockID; ui16 tertiaryData; //double CaveDensity1[9][5][5], CaveDensity2[9][5][5]; std::vector& blockLayers = m_genData->blockLayers; VoxelPosition3D voxPosition = chunk->getVoxelPosition(); chunk->numBlocks = 0; // Generation data IntervalTree::LNode blockDataArray[CHUNK_SIZE]; IntervalTree::LNode tertiaryDataArray[CHUNK_SIZE]; size_t blockDataSize = 0; size_t tertiaryDataSize = 0; ui16 c = 0; bool allAir = true; ui32 layerIndices[CHUNK_LAYER]; // First pass at y = 0. We separate it so we can getBlockLayerIndex a single // time and cut out some comparisons. for (size_t z = 0; z < CHUNK_WIDTH; ++z) { for (size_t x = 0; x < CHUNK_WIDTH; ++x, ++c) { tertiaryData = 0; mapHeight = (int)heightData[c].height; // TODO(Matthew): These statements weren't used, revisit this function to make sure it is behaving correctly. //temperature = heightData[c].temperature; //rainfall = heightData[c].humidity; //tooSteep = (flags & TOOSTEEP) != 0; // TODO(Ben): Fastfloor? height = (int)voxPosition.pos.y; depth = mapHeight - height; // Get depth of voxel // Determine the layer if (depth < 0) { layerIndices[c] = 0; } else { allAir = false; layerIndices[c] = getBlockLayerIndex(depth); } BlockLayer& layer = blockLayers[layerIndices[c]]; // Get the block ID blockID = getBlockID(chunk, c, depth, mapHeight, height, heightData[c], layer); if (blockID != 0) chunk->numBlocks++; // Set up the data arrays if (blockDataSize == 0) { blockDataArray[blockDataSize++].set(c, 1, blockID); tertiaryDataArray[tertiaryDataSize++].set(c, 1, tertiaryData); } else { if (blockID == blockDataArray[blockDataSize - 1].data) { ++blockDataArray[blockDataSize - 1].length; } else { blockDataArray[blockDataSize++].set(c, 1, blockID); } if (tertiaryData == tertiaryDataArray[tertiaryDataSize - 1].data) { ++tertiaryDataArray[tertiaryDataSize - 1].length; } else { tertiaryDataArray[tertiaryDataSize++].set(c, 1, tertiaryData); } } } } // Early exit optimization for solid air chunks if (allAir && blockDataSize == 1 && tertiaryDataSize == 1) { // Set up interval trees blockDataArray[0].length = CHUNK_SIZE; tertiaryDataArray[0].length = CHUNK_SIZE; chunk->blocks.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, blockDataArray, blockDataSize); chunk->tertiary.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, tertiaryDataArray, tertiaryDataSize); return; } // All the rest of the layers. for (size_t y = 1; y < CHUNK_WIDTH; ++y) { for (size_t z = 0; z < CHUNK_WIDTH; ++z) { for (size_t x = 0; x < CHUNK_WIDTH; ++x, ++c) { tertiaryData = 0; hIndex = (c & 0x3FF); // Same as % CHUNK_LAYER mapHeight = heightData[hIndex].height; //temperature = heightData[hIndex].temperature; //rainfall = heightData[hIndex].humidity; // TODO(Ben): Fastfloor? height = (int)(y + voxPosition.pos.y); depth = mapHeight - height; // Get depth of voxel // Check for going up one layer ui16 layerIndex = layerIndices[hIndex]; if (blockLayers[layerIndex].start > (ui32)depth && layerIndex > 0) layerIndex--; // Get the block ID BlockLayer& layer = blockLayers[layerIndex]; blockID = getBlockID(chunk, c, depth, mapHeight, height, heightData[hIndex], layer); //if (tooSteep) dh += 3; // If steep, increase depth // TODO: Modulate dh with noise // TODO(Ben): Check for underground if (blockID != 0) ++chunk->numBlocks; // Add to the data arrays if (blockID == blockDataArray[blockDataSize - 1].data) { ++blockDataArray[blockDataSize - 1].length; } else { blockDataArray[blockDataSize++].set(c, 1, blockID); } if (tertiaryData == tertiaryDataArray[tertiaryDataSize - 1].data) { ++tertiaryDataArray[tertiaryDataSize - 1].length; } else { tertiaryDataArray[tertiaryDataSize++].set(c, 1, tertiaryData); } } } } // Set up interval trees chunk->blocks.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, blockDataArray, blockDataSize); chunk->tertiary.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, tertiaryDataArray, tertiaryDataSize); } void ProceduralChunkGenerator::generateHeightmap(Chunk* chunk, PlanetHeightData* heightData) const { VoxelPosition3D cornerPos3D = chunk->getVoxelPosition(); VoxelPosition2D cornerPos2D; cornerPos2D.pos.x = cornerPos3D.pos.x; cornerPos2D.pos.y = cornerPos3D.pos.z; cornerPos2D.face = cornerPos3D.face; for (int z = 0; z < CHUNK_WIDTH; z++) { for (int x = 0; x < CHUNK_WIDTH; x++) { VoxelPosition2D pos = cornerPos2D; pos.pos.x += x; pos.pos.y += z; m_heightGenerator.generateHeightData(heightData[z * CHUNK_WIDTH + x], pos); } } } // Gets layer in O(log(n)) where n is the number of layers ui32 ProceduralChunkGenerator::getBlockLayerIndex(ui32 depth) const { auto& layers = m_genData->blockLayers; // Binary search ui32 lower = 0; ui32 upper=0; if(!layers.empty()) upper=layers.size()-1; ui32 pos = (lower + upper) / 2; while (lower <= upper) { if (layers[pos].start <= depth && layers[pos].start + layers[pos].width > depth) { // We are in this layer return pos; } else if (layers[pos].start > depth) { upper = pos - 1; } else { lower = pos + 1; } pos = (lower + upper) / 2; } // Just return lowest layer if we fail return layers.size() - 1; } // TODO(Ben): Too many parameters? ui16 ProceduralChunkGenerator::getBlockID(Chunk* chunk, int blockIndex, int depth, int mapHeight VORB_MAYBE_UNUSED, int height, const PlanetHeightData& hd, BlockLayer& layer) const { ui16 blockID = 0; if (depth > 0) { blockID = layer.block; } else if (depth < 0) { // Liquid if (height < 0 && m_genData->liquidBlock) { blockID = m_genData->liquidBlock; } else if (depth == -1) { if (hd.flora != FLORA_ID_NONE) { // We can determine the flora from the heightData during gen. // Only need to store index. chunk->floraToGenerate.push_back(blockIndex); } } } else { blockID = layer.surfaceTransform; } return blockID; } ================================================ FILE: SoA/ProceduralChunkGenerator.h ================================================ /// /// ProceduralChunkGenerator.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 10 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// /// #pragma once #ifndef ProceduralChunkGenerator_h__ #define ProceduralChunkGenerator_h__ struct PlanetGenData; struct PlanetHeightData; struct BlockLayer; class Chunk; #include "SphericalHeightmapGenerator.h" class ProceduralChunkGenerator { public: void init(PlanetGenData* genData); void generateChunk(Chunk* chunk, PlanetHeightData* heightData) const; void generateHeightmap(Chunk* chunk, PlanetHeightData* heightData) const; private: ui32 getBlockLayerIndex(ui32 depth) const; ui16 getBlockID(Chunk* chunk, int blockIndex, int depth, int mapHeight, int height, const PlanetHeightData& hd, BlockLayer& layer) const; PlanetGenData* m_genData = nullptr; SphericalHeightmapGenerator m_heightGenerator; }; #endif // ProceduralChunkGenerator_h__ ================================================ FILE: SoA/ProgramGenDelegate.h ================================================ /// /// ProgramGenDelegate.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 4 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Delegate for generating a shader program /// #pragma once #ifndef ProgramGenDelegate_h__ #define ProgramGenDelegate_h__ #include "ShaderLoader.h" #include #include class ProgramGenDelegate { public: virtual void invoke(Sender sender VORB_MAYBE_UNUSED, void* userData VORB_MAYBE_UNUSED) { printf("Building shader: %s\n", name); if (isFromFile) { program = ShaderLoader::createProgramFromFile(vs, fs, iom); } else { program = ShaderLoader::createProgram(name, vs, fs); } } ProgramGenDelegate() { del = makeDelegate(this, &ProgramGenDelegate::invoke); rpc.data.f = &del; } void init(const cString name, const cString vs, const cString fs) { this->name = name; this->vs = vs; this->fs = fs; rpc.data.userData = nullptr; isFromFile = false; } void initFromFile(const cString name, const cString vertPath, const cString fragPath, vio::IOManager* iom) { this->name = name; this->vs = vertPath; this->fs = fragPath; this->iom = iom; rpc.data.userData = nullptr; isFromFile = true; } const cString name; const cString vs = nullptr; const cString fs = nullptr; bool isFromFile = false; vcore::RPC rpc; Delegate del; vio::IOManager* iom = nullptr; vg::GLProgram program; nString errorMessage; }; #endif // ProgramGenDelegate_h__ ================================================ FILE: SoA/RegionFileManager.cpp ================================================ #include "stdafx.h" #include "RegionFileManager.h" #ifdef VORB_OS_WINDOWS #include //for mkdir windows #include #endif//VORB_OS_WINDOWS #include #include #include #include #include #include "Chunk.h" #include "Errors.h" #include "GameManager.h" #include "VoxelSpaceConversions.h" // Section tags #define TAG_VOXELDATA 0x1 // TODO: Reimplement missing parts and remove VORB_UNUSED tags. // const char TAG_VOXELDATA_STR[4] = { TAG_VOXELDATA, 0, 0, 0 }; inline i32 fileTruncate(i32 fd, i64 size) { #if defined(_WIN32) || defined(_WIN64) return _chsize(fd, (long)size); #else return ftruncate(fd, size); #define _fileno fileno #define _off_t off_t #endif } inline i32 sectorsFromBytes(ui32 bytes) { // Adding 0.1f to be damn sure the cast is right return (i32)(ceil(bytes / (float)SECTOR_SIZE) + 0.1f); } //returns true on error bool checkZlibError(nString message, int zerror) { switch (zerror) { case Z_OK: return false; case Z_STREAM_END: pError("Zlib " + message + " error Z_STREAM_END"); return true; case Z_NEED_DICT: pError("Zlib " + message + " error Z_NEED_DICT"); return true; case Z_ERRNO: pError("Zlib " + message + " error Z_ERRNO"); return true; case Z_STREAM_ERROR: pError("Zlib " + message + " error Z_STREAM_ERROR"); return true; case Z_DATA_ERROR: pError("Zlib " + message + " error Z_DATA_ERROR"); return true; case Z_MEM_ERROR: pError("Zlib " + message + " error Z_MEM_ERROR"); return true; case Z_BUF_ERROR: pError("Zlib " + message + " error Z_BUF_ERROR"); return true; case Z_VERSION_ERROR: pError("Zlib " + message + " error Z_VERSION_ERROR"); return true; } return false; } RegionFileManager::RegionFileManager(const nString& saveDir) : _copySectorsBuffer(nullptr), _maxCacheSize(8), m_saveDir(saveDir), _regionFile(nullptr) { // Empty } RegionFileManager::~RegionFileManager() { clear(); } void RegionFileManager::clear() { for (size_t i = 0; i < _regionFileCacheQueue.size(); i++) { closeRegionFile(_regionFileCacheQueue[i]); } if (_copySectorsBuffer) { delete[] _copySectorsBuffer; _copySectorsBuffer = nullptr; } _regionFileCache.clear(); _regionFileCacheQueue.clear(); _regionFile = nullptr; } // TODO: Investigate why gridPosition isn't used. bool RegionFileManager::openRegionFile(nString region, const ChunkPosition3D& gridPosition VORB_UNUSED, bool create) { nString filePath; struct stat statbuf; RegionFile* rf; if (_regionFile && _regionFile->file && region == _regionFile->region) { return true; } flush(); if (_regionFileCache.size() == _maxCacheSize) { //Remove the oldest region file from the cache rf = _regionFileCacheQueue.front(); _regionFileCacheQueue.pop_front(); _regionFileCache.erase(rf->region); closeRegionFile(rf); } //Check if it is cached auto rit = _regionFileCache.find(region); if (rit != _regionFileCache.end()) { for (auto it = _regionFileCacheQueue.begin(); it != _regionFileCacheQueue.end(); it++) { if ((*it)->region == region) { RegionFile* rf = (*it); _regionFileCacheQueue.erase(it); _regionFileCacheQueue.push_back(rf); break; } } _regionFile = rit->second; return true; } filePath = m_saveDir + "/Region/" + region + ".soar"; //open file if it exists FILE* file = fopen(filePath.c_str(), "rb+"); //If it doesn't exist if (file == nullptr){ //Check if we should create a new region or return false if (create){ file = fopen(filePath.c_str(), "wb+"); //create the file if (file == nullptr){ perror(filePath.c_str()); pError("Failed to create region file "); return false; } } else{ return false; } } _regionFile = new RegionFile; memset(_regionFile, 0, sizeof(RegionFile)); _regionFile->region = region; _regionFile->file = file; _regionFileCache[region] = _regionFile; _regionFileCacheQueue.push_back(_regionFile); _regionFile->fileDescriptor = _fileno(_regionFile->file); //get file descriptor for truncate if needed if (fstat(_regionFile->fileDescriptor, &statbuf) != 0) { pError("Stat call failed for region file open"); //get the file stats return false; } _off_t fileSize = statbuf.st_size; //If the file is new, write an empty header if (fileSize == 0){ //Save the empty header if (saveRegionHeader() == false) return false; _regionFile->totalSectors = 0; fflush(_regionFile->file); } else{ //load header data into the header class if (loadRegionHeader() == false) return false; if ((fileSize - sizeof(RegionFileHeader)) % SECTOR_SIZE){ pError(filePath + ": Region file chunk storage must be multiple of " + std::to_string(SECTOR_SIZE) + ". Remainder = " + std::to_string(sectorsFromBytes(fileSize - sizeof(RegionFileHeader)))); return false; } _regionFile->totalSectors = sectorsFromBytes(fileSize - sizeof(RegionFileHeader)); } return true; } void RegionFileManager::closeRegionFile(RegionFile* regionFile) { if (regionFile->file == nullptr) return; if (_regionFile->isHeaderDirty) { saveRegionHeader(); } fclose(regionFile->file); delete regionFile; } //Attempt to load a chunk. Returns false on failure bool RegionFileManager::tryLoadChunk(Chunk* chunk VORB_UNUSED) { //nString regionString = getRegionString(chunk); ////Open the region file //if (!openRegionFile(regionString, chunk->gridPosition, false)) return false; ////Get the chunk sector offset //ui32 chunkSectorOffset = getChunkSectorOffset(chunk); ////If chunkOffset is zero, it hasnt been saved //if (chunkSectorOffset == 0) { // return false; //} ////Location is not stored zero indexed, so that 0 indicates that it hasnt been saved //chunkSectorOffset -= 1; ////Seek to the chunk header //if (!seekToChunk(chunkSectorOffset)){ // pError("Region: Chunk data fseek C error! " + std::to_string(sizeof(RegionFileHeader)+chunkSectorOffset * SECTOR_SIZE) + " size: " + std::to_string(_regionFile->totalSectors)); // return false; //} ////Get the chunk header //if (!readChunkHeader()) return false; //// Read all chunk data //if (!readChunkData_v0()) return false; // //// Read all tags and process the data //_chunkOffset = 0; //while (_chunkOffset < _chunkBufferSize) { // // Read the tag // ui32 tag = BufferUtils::extractInt(_chunkBuffer, _chunkOffset); // _chunkOffset += sizeof(ui32); // switch (tag) { // case TAG_VOXELDATA: // //Fill the chunk with the aquired data // if (!fillChunkVoxelData(chunk)) return false; // break; // default: // std::cout << "INVALID TAG " << tag << std::endl; // return false; // } //} //return true; return true; } //Saves a chunk to a region file bool RegionFileManager::saveChunk(Chunk* chunk VORB_UNUSED) { ////Used for copying sectors if we need to resize the file //if (_copySectorsBuffer) { // delete[] _copySectorsBuffer; // _copySectorsBuffer = nullptr; //} //nString regionString = getRegionString(chunk); //if (!openRegionFile(regionString, chunk->gridPosition, true)) return false; //ui32 tableOffset; //ui32 chunkSectorOffset = getChunkSectorOffset(chunk, &tableOffset); //i32 numOldSectors; ////If chunkOffset is zero, then we need to add the entry //if (chunkSectorOffset == 0) { // //Set the sector offset in the table // BufferUtils::setInt(_regionFile->header.lookupTable, tableOffset, _regionFile->totalSectors + 1); //we add 1 so that 0 can indicate not saved // _regionFile->isHeaderDirty = true; // chunkSectorOffset = _regionFile->totalSectors; // numOldSectors = 0; // //} else { // //Convert sector offset from 1 indexed to 0 indexed // chunkSectorOffset--; // //seek to the chunk // if (!seekToChunk(chunkSectorOffset)){ // pError("Region: Chunk data fseek save error BB! " + std::to_string(chunkSectorOffset)); // return false; // } // //Get the chunk header // if (!readChunkHeader()) return false; // ui32 oldDataLength = BufferUtils::extractInt(_chunkHeader.dataLength); // numOldSectors = sectorsFromBytes(oldDataLength + sizeof(ChunkHeader)); // if (numOldSectors > _regionFile->totalSectors) { // std::cout << (std::to_string(chunkSectorOffset) + " " + std::to_string(tableOffset) + "Chunk Header Corrupted\n"); // return false; // } //} ////Compress the chunk data //rleCompressChunk(chunk); //zlibCompress(); //i32 numSectors = sectorsFromBytes(_compressedBufferSize); //i32 sectorDiff = numSectors - numOldSectors; ////If we need to resize the number of sectors in the file and this chunk is not at the end of file, ////then we should copy all sectors at the end of the file so we can resize it. This operation should be ////fairly rare. //if ((sectorDiff != 0) && ((chunkSectorOffset + numOldSectors) != _regionFile->totalSectors)) { // if (!seekToChunk(chunkSectorOffset + numOldSectors)){ // pError("Region: Failed to seek for sectorCopy " + std::to_string(chunkSectorOffset) + " " + std::to_string(numOldSectors) + " " + std::to_string(_regionFile->totalSectors)); // return false; // } // _copySectorsBufferSize = (_regionFile->totalSectors - (chunkSectorOffset + numOldSectors)) * SECTOR_SIZE; // _copySectorsBuffer = new ui8[_copySectorsBufferSize]; //for storing all the data that will need to be copied in the end // // readSectors(_copySectorsBuffer, _copySectorsBufferSize); //} ////Set the header data //BufferUtils::setInt(_chunkHeader.compression, COMPRESSION_RLE | COMPRESSION_ZLIB); //BufferUtils::setInt(_chunkHeader.timeStamp, 0); //BufferUtils::setInt(_chunkHeader.dataLength, _compressedBufferSize - sizeof(ChunkHeader)); ////Copy the header data to the write buffer //memcpy(_compressedByteBuffer, &_chunkHeader, sizeof(ChunkHeader)); ////seek to the chunk //if (!seekToChunk(chunkSectorOffset)){ // pError("Region: Chunk data fseek save error GG! " + std::to_string(chunkSectorOffset)); // return false; //} ////Write the header and data //writeSectors(_compressedByteBuffer, (ui32)_compressedBufferSize); ////Keep track of total sectors in file so we can infer filesize //_regionFile->totalSectors += sectorDiff; ////If we need to move some sectors around //if (_copySectorsBuffer) { // if (!seekToChunk(chunkSectorOffset + numSectors)){ // pError("Region: Chunk data fseek save error GG! " + std::to_string(chunkSectorOffset)); // return false; // } // //Write the buffer of sectors // writeSectors(_copySectorsBuffer, _copySectorsBufferSize); // delete[] _copySectorsBuffer; // _copySectorsBuffer = nullptr; // //if the file got smaller // if (sectorDiff < 0){ // //truncate the file // if (fileTruncate(_regionFile->fileDescriptor, sizeof(RegionFileHeader)+_regionFile->totalSectors * SECTOR_SIZE) != 0) { // perror("Region file: Truncate error!\n"); // } // } // //Update the table // ui32 nextChunkSectorOffset; // for (int i = 0; i < REGION_SIZE * 4; i += 4){ // nextChunkSectorOffset = BufferUtils::extractInt(_regionFile->header.lookupTable, i); // //See if the 1 indexed nextChunkSectorOffset is > the 0 indexed chunkSectorOffset // if (nextChunkSectorOffset > (chunkSectorOffset + 1)){ // BufferUtils::setInt(_regionFile->header.lookupTable, i, nextChunkSectorOffset + sectorDiff); // } // } // _regionFile->isHeaderDirty = true; //} //fflush(_regionFile->file); return true; } void RegionFileManager::flush() { if (_regionFile && _regionFile->file) { if (_regionFile->isHeaderDirty) { saveRegionHeader(); fflush(_regionFile->file); } } } bool RegionFileManager::saveVersionFile() { FILE* file; file = fopen((m_saveDir + "/Region/version.dat").c_str(), "wb"); if (!file) return false; SaveVersion currentVersion; BufferUtils::setInt(currentVersion.regionVersion, CURRENT_REGION_VER); fwrite(¤tVersion, 1, sizeof(SaveVersion), file); fclose(file); return true; } bool RegionFileManager::checkVersion() { FILE* file; file = fopen((m_saveDir + "/Region/version.dat").c_str(), "rb"); if (!file) { pError(m_saveDir + "/Region/version.dat not found. Game will assume the version is correct, but it is " + "probable that this save will not work if the version is wrong. If this is a save from 0.1.6 or earlier, then it is " + "only compatible with version 0.1.6 of the game. In that case, please make a new save or download 0.1.6 to play this save."); return saveVersionFile(); } SaveVersion version; fread(&version, 1, sizeof(SaveVersion), file); ui32 regionVersion = BufferUtils::extractInt(version.regionVersion); if (regionVersion != CURRENT_REGION_VER) { return tryConvertSave(regionVersion); } return true; } bool RegionFileManager::readChunkHeader() { if (fread(&(_chunkHeader), 1, sizeof(ChunkHeader), _regionFile->file) != sizeof(ChunkHeader)) { return false; } return true; } bool RegionFileManager::readChunkData_v0() { ui32 dataLength = BufferUtils::extractInt(_chunkHeader.dataLength); if (dataLength > sizeof(_compressedByteBuffer)) { pError("Region voxel input buffer overflow"); return false; } if (fread(_compressedByteBuffer, 1, dataLength, _regionFile->file) != dataLength) { std::cout << "Did not read enough bytes at Z\n"; return false; } _chunkBufferSize = CHUNK_DATA_SIZE + CHUNK_SIZE * 2; int zresult = uncompress(_chunkBuffer, &_chunkBufferSize, _compressedByteBuffer, dataLength); return (!checkZlibError("decompression", zresult)); } int RegionFileManager::rleUncompressArray(ui8* data, ui32& byteIndex, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc) { ui8 value; ui16 runSize; int index; int i = 0; //y int j = jStart; //z int k = kStart; //x int blockCounter = 0; //Read block data while (blockCounter < CHUNK_SIZE){ //Grab a run of RLE data runSize = BufferUtils::extractShort(_chunkBuffer, byteIndex); value = _chunkBuffer[byteIndex + 2]; for (int q = 0; q < runSize; q++){ index = i * CHUNK_LAYER + j * jMult + k * kMult; if (index >= CHUNK_SIZE){ pError("Chunk File Corrupted! Index >= 32768. (" + std::to_string(index) + ") " + std::to_string(q) + " " + std::to_string(runSize) + " " + std::to_string(blockCounter)); return 1; } data[index] = value; blockCounter++; k += kInc; if (k == kEnd){ k = kStart; j += jInc; if (j == jEnd){ j = jStart; i++; } } } byteIndex += 3; } return 0; } int RegionFileManager::rleUncompressArray(ui16* data, ui32& byteIndex, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc) { ui16 value; ui16 runSize; int index; int i = 0; //y int j = jStart; //z int k = kStart; //x int blockCounter = 0; //Read block data while (blockCounter < CHUNK_SIZE){ //Grab a run of RLE data runSize = BufferUtils::extractShort(_chunkBuffer, byteIndex); value = BufferUtils::extractShort(_chunkBuffer, byteIndex + 2); for (int q = 0; q < runSize; q++){ index = i * CHUNK_LAYER + j * jMult + k * kMult; if (index >= CHUNK_SIZE){ pError("Chunk File Corrupted! Index >= 32768. (" + std::to_string(index) + ") " + std::to_string(q) + " " + std::to_string(runSize) + " " + std::to_string(blockCounter)); return 1; } data[index] = value; blockCounter++; k += kInc; if (k == kEnd){ k = kStart; j += jInc; if (j == jEnd){ j = jStart; i++; } } } byteIndex += 4; } return 0; } bool RegionFileManager::fillChunkVoxelData(Chunk* chunk VORB_UNUSED) { // TODO(Ben): Fix //ui8 lightVal; //int blockIndex; //int jStart, jEnd, jInc; //int kStart, kEnd, kInc; //int jMult, kMult; //chunk->voxelMapData->getIterationConstants(jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); //chunk->numBlocks = 0; //if (rleUncompressArray(_blockIDBuffer, _chunkOffset, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc)) return false; //if (rleUncompressArray(_lampLightBuffer, _chunkOffset, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc)) return false; //if (rleUncompressArray(_sunlightBuffer, _chunkOffset, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc)) return false; //if (rleUncompressArray(_tertiaryDataBuffer, _chunkOffset, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc)) return false; ////Node buffers, reserving maximum memory so we don't ever need to reallocate. Static so that the memory persists. //static std::vector::LNode> blockIDNodes(CHUNK_SIZE, IntervalTree::LNode(0, 0, 0)); //static std::vector::LNode> lampLightNodes(CHUNK_SIZE, IntervalTree::LNode(0, 0, 0)); //static std::vector::LNode> sunlightNodes(CHUNK_SIZE, IntervalTree::LNode(0, 0, 0)); //static std::vector::LNode> tertiaryDataNodes(CHUNK_SIZE, IntervalTree::LNode(0, 0, 0)); ////Make the size 0 //blockIDNodes.clear(); //lampLightNodes.clear(); //sunlightNodes.clear(); //tertiaryDataNodes.clear(); // // chunk->_blockIDContainer.initFromSortedArray() //ui16 blockID; //ui16 lampLight; //ui8 sunlight; //ui16 tertiaryData; ////Add first nodes //blockIDNodes.push_back(IntervalTree::LNode(0, 1, _blockIDBuffer[0])); //lampLightNodes.push_back(IntervalTree::LNode(0, 1, _lampLightBuffer[0])); //sunlightNodes.push_back(IntervalTree::LNode(0, 1, _sunlightBuffer[0])); //tertiaryDataNodes.push_back(IntervalTree::LNode(0, 1, _tertiaryDataBuffer[0])); ////Construct the node vectors //for (int i = 1; i < CHUNK_SIZE; i++) { // blockID = _blockIDBuffer[i]; // lampLight = _lampLightBuffer[i]; // sunlight = _sunlightBuffer[i]; // tertiaryData = _tertiaryDataBuffer[i]; // if (blockID != 0) chunk->numBlocks++; // // if (GETBLOCK(blockID).spawnerVal || GETBLOCK(blockID).sinkVal){ // chunk->spawnerBlocks.push_back(i); // } // if (blockID == blockIDNodes.back().data) { // blockIDNodes.back().length++; // } else { // blockIDNodes.push_back(IntervalTree::LNode(i, 1, blockID)); // } // if (lampLight == lampLightNodes.back().data) { // lampLightNodes.back().length++; // } else { // lampLightNodes.push_back(IntervalTree::LNode(i, 1, lampLight)); // } // if (sunlight == sunlightNodes.back().data) { // sunlightNodes.back().length++; // } else { // sunlightNodes.push_back(IntervalTree::LNode(i, 1, sunlight)); // } // if (tertiaryData == tertiaryDataNodes.back().data) { // tertiaryDataNodes.back().length++; // } else { // tertiaryDataNodes.push_back(IntervalTree::LNode(i, 1, tertiaryData)); // } //} //chunk->_blockIDContainer.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, blockIDNodes); //chunk->_lampLightContainer.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, lampLightNodes); //chunk->_sunlightContainer.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, sunlightNodes); //chunk->_tertiaryDataContainer.initFromSortedArray(vvox::VoxelStorageState::INTERVAL_TREE, tertiaryDataNodes); return true; } //Saves the header for the region file bool RegionFileManager::saveRegionHeader() { //Go back to beginning of file to save the header if (!seek(0)) { pError("Fseek error: could not seek to start. Save file is corrupted!\n"); return false; } //Save the header if (fwrite(&(_regionFile->header), 1, sizeof(RegionFileHeader), _regionFile->file) != sizeof(RegionFileHeader)){ pError("Region write error: could not write loc buffer. Save file is corrupted!\n"); return false; } _regionFile->isHeaderDirty = false; return true; } //Loads the header for the region file and stores it in the region file class bool RegionFileManager::loadRegionHeader() { if (!seek(0)){ pError("Region fseek error F could not seek to start\n"); return false; } if (fread(&(_regionFile->header), 1, sizeof(RegionFileHeader), _regionFile->file) != sizeof(RegionFileHeader)){ //read the whole buffer in pError("Region read error: could not read region header\n"); return false; } return true; } void RegionFileManager::rleCompressArray(ui8* data, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc) { int count = 1; int tot = 0; ui16 curr = data[jStart*jMult + kStart*kMult]; int index; int z = 0; bool first = true; for (int i = 0; i < CHUNK_WIDTH; i++){ //y for (int j = jStart; j != jEnd; j += jInc){ //z for (int k = kStart; k != kEnd; k += kInc){ //x z++; if (!first) { index = i*CHUNK_LAYER + j*jMult + k*kMult; if (data[index] != curr){ _chunkBuffer[_bufferSize++] = (ui8)(count & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((count & 0xFF00) >> 8); _chunkBuffer[_bufferSize++] = (ui8)curr; tot += count; curr = data[index]; count = 1; } else{ count++; } } else { first = false; } } } } _chunkBuffer[_bufferSize++] = (ui8)(count & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((count & 0xFF00) >> 8); tot += count; _chunkBuffer[_bufferSize++] = (ui8)curr; } void RegionFileManager::rleCompressArray(ui16* data, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc) { int count = 1; ui16 curr = data[jStart*jMult + kStart*kMult]; int index; int tot = 0; bool first = true; for (int i = 0; i < CHUNK_WIDTH; i++){ //y for (int j = jStart; j != jEnd; j += jInc){ //z for (int k = kStart; k != kEnd; k += kInc){ //x if (!first){ index = i*CHUNK_LAYER + j*jMult + k*kMult; if (data[index] != curr){ _chunkBuffer[_bufferSize++] = (ui8)(count & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((count & 0xFF00) >> 8); _chunkBuffer[_bufferSize++] = (ui8)(curr & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((curr & 0xFF00) >> 8); tot += count; curr = data[index]; count = 1; } else{ count++; } } else{ first = false; } } } } _chunkBuffer[_bufferSize++] = (ui8)(count & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((count & 0xFF00) >> 8); _chunkBuffer[_bufferSize++] = (ui8)(curr & 0xFF); _chunkBuffer[_bufferSize++] = (ui8)((curr & 0xFF00) >> 8); tot += count; } bool RegionFileManager::rleCompressChunk(Chunk* chunk VORB_UNUSED) { // TODO(Ben): Fix //ui16* blockIDData; //ui8* sunlightData; //ui16* lampLightData; //ui16* tertiaryData; ////Need to lock so that nobody modifies the interval tree out from under us //chunk->lock(); //if (chunk->_blockIDContainer.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { // blockIDData = _blockIDBuffer; // chunk->_blockIDContainer.uncompressIntoBuffer(blockIDData); //} else { // blockIDData = chunk->_blockIDContainer.getDataArray(); //} //if (chunk->_lampLightContainer.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { // lampLightData = _lampLightBuffer; // chunk->_lampLightContainer.uncompressIntoBuffer(lampLightData); //} else { // lampLightData = chunk->_lampLightContainer.getDataArray(); //} //if (chunk->_sunlightContainer.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { // sunlightData = _sunlightBuffer; // chunk->_sunlightContainer.uncompressIntoBuffer(sunlightData); //} else { // sunlightData = chunk->_sunlightContainer.getDataArray(); //} //if (chunk->_tertiaryDataContainer.getState() == vvox::VoxelStorageState::INTERVAL_TREE) { // tertiaryData = _tertiaryDataBuffer; // chunk->_tertiaryDataContainer.uncompressIntoBuffer(tertiaryData); //} else { // tertiaryData = chunk->_tertiaryDataContainer.getDataArray(); //} //chunk->unlock(); //_bufferSize = 0; //// Set the tag //memcpy(_chunkBuffer, TAG_VOXELDATA_STR, 4); //_bufferSize += 4; //int jStart, jEnd, jInc; //int kStart, kEnd, kInc; //int jMult, kMult; //chunk->voxelMapData->getIterationConstants(jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); //rleCompressArray(blockIDData, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); //rleCompressArray(lampLightData, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); //rleCompressArray(sunlightData, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); //rleCompressArray(tertiaryData, jStart, jMult, jEnd, jInc, kStart, kMult, kEnd, kInc); return true; } bool RegionFileManager::zlibCompress() { _compressedBufferSize = CHUNK_DATA_SIZE + CHUNK_SIZE * 2; //Compress the data, and leave space for the uncompressed chunk header int zresult = compress2(_compressedByteBuffer + sizeof(ChunkHeader), &_compressedBufferSize, _chunkBuffer, _bufferSize, 6); _compressedBufferSize += sizeof(ChunkHeader); return (!checkZlibError("compression", zresult)); } // TODO: Implement this and remove VORB_UNUSED tags. bool RegionFileManager::tryConvertSave(ui32 regionVersion VORB_UNUSED) { pError("Invalid region file version!"); return false; } //Writes sector data, be sure to fseek to the correct position first bool RegionFileManager::writeSectors(ui8* srcBuffer, ui32 size) { if (size % SECTOR_SIZE) { ui32 padLength = SECTOR_SIZE - size % SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; size += padLength; } if (fwrite(srcBuffer, 1, size, _regionFile->file) != size) { pError("Chunk Saving: Did not write enough bytes at A " + std::to_string(size)); return false; } return true; } //Read sector data, be sure to fseek to the correct position first bool RegionFileManager::readSectors(ui8* dstBuffer, ui32 size) { if (fread(dstBuffer, 1, size, _regionFile->file) != size) { pError("Chunk Loading: Did not read enough bytes at A " + std::to_string(size) + " " + std::to_string(_regionFile->totalSectors)); return false; } return true; } bool RegionFileManager::seek(ui32 byteOffset) { return (fseek(_regionFile->file, byteOffset, SEEK_SET) == 0); } bool RegionFileManager::seekToChunk(ui32 chunkSectorOffset) { //seek to the chunk return seek(sizeof(RegionFileHeader) + chunkSectorOffset * SECTOR_SIZE); } ui32 RegionFileManager::getChunkSectorOffset(Chunk* chunk VORB_UNUSED, ui32* retTableOffset VORB_UNUSED) { //const ChunkPosition3D& gridPos = chunk->gridPosition; //int x = gridPos.pos.x % REGION_WIDTH; //int y = gridPos.pos.y % REGION_WIDTH; //int z = gridPos.pos.z % REGION_WIDTH; ////modulus is weird in c++ for negative numbers //if (x < 0) x += REGION_WIDTH; //if (y < 0) y += REGION_WIDTH; //if (z < 0) z += REGION_WIDTH; //ui32 tableOffset = 4 * (x + z * REGION_WIDTH + y * REGION_LAYER); ////If the caller asked for the table offset, return it //if (retTableOffset) *retTableOffset = tableOffset; //return BufferUtils::extractInt(_regionFile->header.lookupTable, tableOffset); return 0; } nString RegionFileManager::getRegionString(Chunk *ch) { const ChunkPosition3D& gridPos = ch->getChunkPosition(); return "r." + std::to_string(fastFloor((float)gridPos.pos.x / REGION_WIDTH)) + "." + std::to_string(fastFloor((float)gridPos.pos.y / REGION_WIDTH)) + "." + std::to_string(fastFloor((float)gridPos.pos.z / REGION_WIDTH)); } ================================================ FILE: SoA/RegionFileManager.h ================================================ #pragma once #include #include #include #include #include "Constants.h" #include "VoxelCoordinateSpaces.h" //Size of a sector in bytes #define SECTOR_SIZE 512 //Make sure REGION_WIDTH is 2 ^ RSHIFT and //REGION_SIZE is REGION_WIDTH ^ 3 and //REGION_LAYER is REGION_WIDTH ^ 2 #define RSHIFT 4 #define REGION_WIDTH 16 #define REGION_LAYER 256 #define REGION_SIZE 4096 #define REGION_VER_0 1000 #define CURRENT_REGION_VER REGION_VER_0 #define CHUNK_DATA_SIZE (CHUNK_SIZE * 4) //right now a voxel is 4 bytes #define COMPRESSION_RLE 0x1 #define COMPRESSION_ZLIB 0x10 //All data is stored in byte arrays so we can force it to be saved in big-endian class ChunkHeader { public: ui8 compression[4]; ui8 timeStamp[4]; ui8 dataLength[4]; //length of the data }; class RegionFileHeader { public: ui8 lookupTable[REGION_SIZE * 4]; }; class RegionFile { public: RegionFileHeader header; nString region; FILE* file; int fileDescriptor; i32 totalSectors; bool isHeaderDirty; }; class SaveVersion { public: ui8 regionVersion[4]; ui8 chunkVersion[4]; }; class Chunk; class RegionFileManager { public: RegionFileManager(const nString& saveDir); ~RegionFileManager(); void clear(); bool openRegionFile(nString region, const ChunkPosition3D& gridPosition, bool create); bool tryLoadChunk(Chunk* chunk); bool saveChunk(Chunk* chunk); void flush(); bool saveVersionFile(); bool checkVersion(); private: void closeRegionFile(RegionFile* regionFile); bool readChunkHeader(); bool readChunkData_v0(); int rleUncompressArray(ui8* data, ui32& byteIndex, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc); int rleUncompressArray(ui16* data, ui32& byteIndex, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc); bool fillChunkVoxelData(Chunk* chunk); bool saveRegionHeader(); bool loadRegionHeader(); void rleCompressArray(ui8* data, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc); void rleCompressArray(ui16* data, int jStart, int jMult, int jEnd, int jInc, int kStart, int kMult, int kEnd, int kInc); bool rleCompressChunk(Chunk* chunk); bool zlibCompress(); bool tryConvertSave(ui32 regionVersion); bool writeSectors(ui8* srcBuffer, ui32 size); bool readSectors(ui8* dstBuffer, ui32 size); bool seek(ui32 byteOffset); bool seekToChunk(ui32 chunkSectorOffset); ui32 getChunkSectorOffset(Chunk* chunk, ui32* retTableOffset = nullptr); nString getRegionString(Chunk* chunk); //Byte buffer for reading chunk data ui32 _bufferSize; ui8 _chunkBuffer[CHUNK_DATA_SIZE]; //Byte buffer for compressed data. It is slightly larger because of worst case with RLE uLongf _compressedBufferSize; ui8 _compressedByteBuffer[CHUNK_DATA_SIZE + CHUNK_SIZE * 4 + sizeof(ChunkHeader)]; //Dynamic byte buffer used in copying contents of a file for resize // ui32 _copySectorsBufferSize; ui8* _copySectorsBuffer; // ui16 _blockIDBuffer[CHUNK_SIZE]; // ui8 _sunlightBuffer[CHUNK_SIZE]; // ui16 _lampLightBuffer[CHUNK_SIZE]; // ui16 _tertiaryDataBuffer[CHUNK_SIZE]; // ui8 _chunkHeaderBuffer[sizeof(ChunkHeader)]; // ui8 _regionFileHeaderBuffer[sizeof(RegionFileHeader)]; ui32 _maxCacheSize; // ui32 _chunkOffset; ///< Offset into the chunk data uLongf _chunkBufferSize; std::map _regionFileCache; std::deque _regionFileCacheQueue; nString m_saveDir; RegionFile* _regionFile; ChunkHeader _chunkHeader; }; ================================================ FILE: SoA/RenderUtils.h ================================================ /// /// RenderUtils.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 3 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Some simple utils for rendering /// #pragma once #ifndef RenderUtils_h__ #define RenderUtils_h__ #include "stdafx.h" /// Sets translation for a matrix relative to relativePos. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f64v3 &position, const f64v3& relativePos) { matrix[3][0] = (f32)(position.x - relativePos.x); matrix[3][1] = (f32)(position.y - relativePos.y); matrix[3][2] = (f32)(position.z - relativePos.z); } /// Sets translation for a matrix relative to relativePos. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f32v3 &position, const f32v3& relativePos) { matrix[3][0] = (position.x - relativePos.x); matrix[3][1] = (position.y - relativePos.y); matrix[3][2] = (position.z - relativePos.z); } /// Sets translation for a matrix relative to relativePos. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f32v3 &position, const f64v3& relativePos) { matrix[3][0] = (f32)((f64)position.x - relativePos.x); matrix[3][1] = (f32)((f64)position.y - relativePos.y); matrix[3][2] = (f32)((f64)position.z - relativePos.z); } /// Sets translation for a matrix. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f32 x, const f32 y, const f32 z) { matrix[3][0] = x; matrix[3][1] = y; matrix[3][2] = z; } /// Sets translation for a matrix. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const double x, const double y, const double z) { matrix[3][0] = (f32)x; matrix[3][1] = (f32)y; matrix[3][2] = (f32)z; } /// Sets translation for a matrix. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f32v3& trans) { matrix[3][0] = trans.x; matrix[3][1] = trans.y; matrix[3][2] = trans.z; } /// Sets translation for a matrix. Will overwrite existing translation inline void setMatrixTranslation(f32m4& matrix, const f64v3& trans) { matrix[3][0] = (f32)trans.x; matrix[3][1] = (f32)trans.y; matrix[3][2] = (f32)trans.z; } /// Sets scale for a matrix. Will overwrite existing scale inline void setMatrixScale(f32m4& matrix, const f32v3 &scale) { matrix[0][0] = scale.x; matrix[1][1] = scale.y; matrix[2][2] = scale.z; } /// Sets scale for a matrix. Will overwrite existing scale inline void setMatrixScale(f32m4& matrix, const f32 scaleX, const f32 scaleY, const f32 scaleZ) { matrix[0][0] = scaleX; matrix[1][1] = scaleY; matrix[2][2] = scaleZ; } #endif // RenderUtils_h__ ================================================ FILE: SoA/Resources/resources.rc ================================================ MAINICON ICON DISCARDABLE "SOA.ico" ================================================ FILE: SoA/SOA.vcxproj ================================================  DebugXP Win32 DebugXP x64 Debug Win32 Debug x64 ReleaseXP Win32 ReleaseXP x64 Release Win32 Release x64 {588491F3-69BA-40E7-8CE8-2382A64D86E3} soa SoA Application true v140 MultiByte Application true v140_xp MultiByte Application true v140 MultiByte Application true v140 MultiByte Application false v140 true MultiByte Application false v140_xp true MultiByte Application false v140 true MultiByte Application false v140 true MultiByte $(SolutionDir)deps\win32\include;$(SolutionDir);$(SolutionDir)deps\include;$(IncludePath) $(SolutionDir)deps\win32\lib;$(LibraryPath) $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA $(SolutionDir)deps\win32\include;$(SolutionDir);$(SolutionDir)deps\include;$(IncludePath) $(SolutionDir)deps\win32\lib;$(LibraryPath) $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA $(SolutionDir)deps\win32\include;$(IncludePath) $(SolutionDir)deps\win32\lib;$(SolutionDir)bin\lib\$(PlatformName)\$(PlatformTarget)\$(Configuration)\;$(LibraryPath) $(Configuration) $(SolutionDir)bin\$(PlatformName)\$(PlatformTarget)\$(Configuration)\ $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA $(SolutionDir)deps\win32\include;$(IncludePath) $(SolutionDir)deps\win32\lib;$(SolutionDir)bin\lib\$(PlatformName)\$(PlatformTarget)\$(Configuration)\;$(LibraryPath) $(Configuration) $(SolutionDir)bin\$(PlatformName)\$(PlatformTarget)\$(Configuration)\ $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA SoA $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ SoA $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ SoA SoA $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ SoA $(VC_IncludePath);$(WindowsSDK_IncludePath); $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64); $(SolutionDir)bin\$(PlatformName)\$(Configuration)\SoA\ $(SolutionDir)obj\$(PlatformName)\$(Configuration)\SoA\ Level3 Disabled $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) VORB_USING_PCH;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;VORB_IMPL_GRAPHICS_OPENGL;%(PreprocessorDefinitions) VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;%(PreprocessorDefinitions) Use false false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb-d.lib;%(AdditionalDependencies) Console true $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 Disabled $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) VORB_USING_PCH;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;VORB_IMPL_GRAPHICS_OPENGL;%(PreprocessorDefinitions) VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;XP;%(PreprocessorDefinitions) Use false false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb-d.lib;%(AdditionalDependencies) Console true $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 Disabled $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;%(PreprocessorDefinitions) Use false false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true Vorb-d.lib;%(AdditionalDependencies) Console true $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 Disabled $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;DEBUG;%(PreprocessorDefinitions) Use false false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true Vorb-d.lib;%(AdditionalDependencies) Console true $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 MaxSpeed true true $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) true Speed OnlyExplicitInline VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) Use false false StreamingSIMDExtensions2 -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb.lib;%(AdditionalDependencies) Console $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) true false robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 MaxSpeed true true $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) true Speed OnlyExplicitInline VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;XP;%(PreprocessorDefinitions) Use false false StreamingSIMDExtensions2 -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb.lib;%(AdditionalDependencies) Console $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) true false robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 MaxSpeed true true $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) true Speed OnlyExplicitInline VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) Use false false StreamingSIMDExtensions2 -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb.lib;%(AdditionalDependencies) Console $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) true false robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Level3 MaxSpeed true true $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) true Speed OnlyExplicitInline VORB_USING_PCH;VORB_USING_SCRIPT;VORB_IMPL_GRAPHICS_OPENGL;BT_USE_DOUBLE_PRECISION;WINDOWS;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) Use false false StreamingSIMDExtensions2 -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) true true true Vorb.lib;%(AdditionalDependencies) Console $(SolutionDir)deps\lib\$(PlatformName)\;%(AdditionalLibraryDirectories) true false robocopy /XO /E "$(SolutionDir)deps\dll\$(PlatformName)\$(Configuration)\ " "$(OutDir) " * /XD ".git" EXIT 0 Copying dependencies to output directory cd $(SolutionDir) $(SolutionDir)VorbCopy.bat Run the Vorb copy script PerMonitorHighDPIAware Create Create Create Create Create Create Create Create ================================================ FILE: SoA/SOA.vcxproj.filters ================================================  {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {525edd3f-153d-4d7e-b2c5-811fe8ece78b} {2c08331c-74a3-41e5-a2ae-d7f834b44a9c} {47d2e9d3-5782-498b-8946-54c76c398ccd} {a64b4a4f-cf12-45af-bea5-915fe1edb71f} {3b6faecb-6048-43b9-b958-0ec1519bcfd4} {8004f8be-0c9d-4b46-8de3-95f601224cb7} {16458b45-378f-4940-9d6b-22626b459c7d} {bdc5c7ad-0882-4888-bf85-a2b6df8ca6c8} {69183acd-97b7-4ccf-8b3d-bf869213d785} {6ed11451-4fd0-4e0b-bb3d-7c837c30e6b0} {c500d35d-e264-4ecb-91ae-63e32462a5f5} {28e31f12-788f-4d75-83c4-90578c32fd26} {944280de-51d1-4204-8783-de9d490e0597} {006799fc-14f6-4bb3-9ba5-ce6e1a519f66} {cf258b53-3a8a-47e2-bff3-cfe5f3bcd843} {b2f9c117-3cdf-4c36-9025-eab0f43b9857} {66e53e02-b172-41ff-9611-8bc3bb0cc2e6} {41066a19-1d18-4603-a153-87d0e8ee97c3} {d0120966-37e8-471d-bdce-519b4af45baf} {a6444217-9b9d-47d7-93ef-b7677b135940} {f13fbb0d-44a4-47c5-ab3e-37ea49ff2371} {f1c18989-f573-4fa6-ba8b-ebcaa46f53d1} {6da0d3a4-a60f-4ed2-804e-c655cc23292d} {27b0b7c0-0ccd-46a8-be5f-bace1ed61570} {f94617d8-52f7-4073-97a5-2d26439a6c08} {a8613ee5-a6ad-4b60-8567-0a15bb9ae82c} {f10c4941-dfe2-4bfb-a029-a82ec2a097dd} {27507245-c888-4a05-b0ca-9890d99db1a2} {0d210193-9560-40f1-a16b-af80064b7293} {da75023d-4b8b-4790-af72-c6535569b583} {09cebf8a-9329-4dea-aac4-4d56d9dd2f37} {ed09a071-ec97-4fd2-922b-f2c36eba05dc} {853856ea-242e-44d2-8339-eaf88777864f} {22a36be7-1c86-4e53-bdde-77f979115e26} {877c328b-8bd7-4523-952d-f4bbb6408408} {07d56bf0-8858-45a6-bcf5-6fe4f58dc323} {1c1faa8a-6bbf-4bd3-a594-ba162a1a31a9} {9ca22bd9-7b16-4f82-914d-635a6afa8e69} {85765d6e-8575-45fa-82db-357787c61b39} {73eeb8a0-c66a-4fc2-bd59-edcbe41b3993} {70862515-6514-4574-9caf-db361861ced8} {29e314c8-5ae6-4581-9595-811643a15e11} {f52bd506-1a48-4733-81fd-e5ccf12ec387} {5a883327-0095-495a-b35f-f80c5fec3f94} {3786d69c-53eb-4f18-9fa3-f5b24d8dbfa1} {04e223d6-921c-418a-8fef-01869cbc81d1} {8ba56a3b-89be-47d4-a283-21b82f6ebbfa} {f35809b6-67de-47ce-990b-6b313f00bfac} {cbcce115-6609-45f8-8bf5-9f86d0c7358f} {3d4b1864-12e0-42f6-93e0-9dd41f775f53} {67ff73ec-bf0a-46da-a71d-8821a7acbd76} {452e9a89-0e3d-42af-827a-14a1b726bc20} {05a33879-3c8f-4d59-a153-cc3c410fcc98} {c4cbe6c3-81db-48d5-9680-8b949bc631a6} {705cf454-4163-4629-a5a8-b8f21eceeea7} {548ab1e5-861c-44a6-9081-0769265688b2} {29e1a60e-9cde-4091-b045-d6c803be19ca} {771788c4-8294-4b8d-b65c-616f4371e8c9} {65cf4844-1033-4e01-a560-63f43e8b3660} {57f94b81-194d-4aa7-91bb-c4855307dd0e} {7438a4f1-7ecc-4b92-bc2a-6865f0686297} {8abe075e-c1df-4773-9ffd-b200f0dd9087} SOA Files\Screens SOA Files\Ext SOA Files\Voxel SOA Files\Voxel SOA Files\Rendering SOA Files\Voxel\Generation SOA Files\Data SOA Files\Voxel\Meshing SOA Files\Rendering SOA Files\Voxel SOA Files\Game\Physics SOA Files\Game SOA Files\Rendering SOA Files\Game\Dev SOA Files\Rendering\Dev SOA Files\Ext SOA Files\Data\IO Utils SOA Files\Rendering SOA Files\Game SOA Files\Rendering SOA Files\Ext\Input SOA Files\Voxel SOA Files\Screens\Load SOA Files\Rendering SOA Files\Ext\ADT SOA Files\Data SOA Files\Rendering SOA Files SOA Files\Voxel\Utils SOA Files\Voxel\Generation SOA Files\Voxel\Utils SOA Files\Voxel\Utils SOA Files\Voxel\Utils SOA Files\Game SOA Files\Voxel\WSO SOA Files\Voxel\WSO SOA Files\Voxel\WSO SOA Files\Voxel\WSO SOA Files\Data\IO Utils SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Screens\Load SOA Files\Screens\Init SOA Files\Screens\Menu SOA Files\Screens\Load SOA Files\Rendering\Wrappers SOA Files\Game\Tools SOA Files\Game\Tools SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Utils SOA Files\Voxel\Meshing SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Game\Tools\AR SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Tasking SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Utils SOA Files\Voxel\Tasking SOA Files\Screens\Gameplay SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Screens\Test SOA Files\Voxel SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel SOA Files\Screens\Menu SOA Files\ECS\Systems SOA Files\ECS\Systems SOA Files\Game SOA Files\Game SOA Files\Game SOA Files\ECS\Components SOA Files\Sound\Music SOA Files\Game SOA Files\Sound\Ambience SOA Files\Sound\Ambience SOA Files\Sound\Ambience SOA Files\ECS\Components SOA Files\ECS\Systems SOA Files\ECS\Assemblages SOA Files\ECS\Assemblages SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\Voxel\Mapping SOA Files\Voxel\Mapping SOA Files\Voxel\Mapping SOA Files\Rendering\Delegates SOA Files\Game\Universe\Generation SOA Files\Game\Universe\Generation SOA Files\Game\Universe SOA Files\ECS\Updaters\SpaceSystem SOA Files\Rendering\Universe SOA Files\Rendering\Universe SOA Files\Game\Universe SOA Files\Rendering\Universe SOA Files\Rendering\Universe SOA Files\Game\Universe SOA Files SOA Files\Rendering\Universe SOA Files\Rendering SOA Files\Rendering SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Screens\Load\Tasks SOA Files\Screens\Load\Tasks SOA Files\Screens\Gameplay SOA Files\Rendering SOA Files\Rendering\Wrappers SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\ECS\ComponentTables SOA Files\Data SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Rendering\Components SOA Files\Game SOA Files\Screens SOA Files\Client\Asset SOA Files\Client\Asset SOA Files\Rendering SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Game SOA Files\Rendering\RenderPipelines SOA Files\Rendering\RenderPipelines SOA Files\Screens\Load SOA Files\Screens\Load SOA Files\Screens\Test SOA Files\Voxel\API SOA Files\Screens\Test SOA Files\Voxel\Mapping SOA Files\Voxel SOA Files\Voxel\Allocation SOA Files\Voxel\Generation SOA Files\Voxel SOA Files\Voxel SOA Files\Voxel SOA Files\Rendering SOA Files\Screens\Test SOA Files\Data\IO Utils SOA Files\Ext\Input SOA Files\Data SOA Files\Game\Universe\Generation SOA Files\Rendering SOA Files\Rendering SOA Files\Voxel\Texturing SOA Files\Voxel\Texturing SOA Files\Voxel\Texturing SOA Files\Voxel\Texturing SOA Files\Voxel\Texturing SOA Files\Screens\Menu SOA Files\Rendering SOA Files\Game\Universe\Generation SOA Files SOA Files\Console SOA Files\Console SOA Files\Modding SOA Files\Modding SOA Files\Modding SOA Files\Voxel\Tasking SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Screens\Load\Tasks SOA Files\Game\Universe\Generation SOA Files\Game\Universe\Generation SOA Files\Game\Universe SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Game SOA Files\ECS SOA Files\ECS\ComponentBuilders SOA Files\ECS\ComponentBuilders SOA Files\Screens\Test SOA Files\Game\Universe SOA Files\Game\Universe\Generation SOA Files\Voxel\Generation SOA Files\Voxel\Access SOA Files\Voxel\Access SOA Files\Voxel\Access SOA Files\Voxel\Access SOA Files\Console SOA Files\ECS\Updaters\GameSystem SOA Files\Client SOA Files\ECS\Updaters\GameSystem SOA Files\Voxel\Meshing SOA Files\ECS\Updaters\GameSystem SOA Files\Game\Objects SOA Files\Game\Universe\Generation SOA Files\Game\Universe\Generation SOA Files\Voxel SOA Files SOA Files\Screens SOA Files\Voxel SOA Files\Voxel SOA Files\Rendering SOA Files\Voxel\Generation SOA Files\Data SOA Files\Voxel\Meshing SOA Files\Rendering SOA Files\Voxel SOA Files\Game\Physics SOA Files\Rendering SOA Files\Game\Dev SOA Files\Rendering\Dev SOA Files\Ext SOA Files\Data\IO Utils SOA Files\Rendering SOA Files\Game SOA Files\Rendering SOA Files\Ext\Input SOA Files\Screens\Load SOA Files SOA Files\Data SOA Files SOA Files\Voxel\Utils SOA Files\Voxel\Generation SOA Files\Voxel\Utils SOA Files\Voxel\Utils SOA Files\Game SOA Files\Voxel\WSO SOA Files\Voxel\WSO SOA Files\Voxel\WSO SOA Files\Data\IO Utils SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Screens\Init SOA Files\Screens\Load SOA Files\Screens\Menu SOA Files\Screens\Load SOA Files\Game\Tools SOA Files\Game\Tools SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Meshing SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Game\Tools\AR SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Tasking SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Tasking SOA Files\Screens\Gameplay SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel SOA Files\Screens\Dev SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel SOA Files\Screens\Test SOA Files\Screens\Menu SOA Files\ECS\Systems SOA Files\Game SOA Files\Game SOA Files\Game SOA Files\Sound\Music SOA Files\Game SOA Files\Sound\Ambience SOA Files\Sound\Ambience SOA Files\Sound\Ambience SOA Files\ECS\Systems SOA Files\ECS\Assemblages SOA Files\ECS\Assemblages SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\ECS\Updaters\SpaceSystem SOA Files\Voxel\Mapping SOA Files\Voxel\Mapping SOA Files\Game\Universe\Generation SOA Files\Game\Universe SOA Files\ECS\Updaters\SpaceSystem SOA Files\Rendering\Universe SOA Files\Rendering\Universe SOA Files\Rendering\Universe SOA Files\Rendering\Universe SOA Files\Game\Universe SOA Files\Rendering\Universe SOA Files\Rendering SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Screens\Gameplay SOA Files\Rendering SOA Files\Ext\Input SOA Files\Rendering\Wrappers SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\Rendering\Components SOA Files\ECS\ComponentTables SOA Files\Data SOA Files\Screens\Test SOA Files\Screens\Test SOA Files\Rendering\Components SOA Files\Game SOA Files\Client\Asset SOA Files\Client\Asset SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Rendering\RenderPipelines SOA Files\Rendering\RenderPipelines SOA Files\Screens\Load SOA Files\Screens\Load SOA Files\Voxel\API SOA Files\Screens\Test SOA Files\Voxel\Allocation SOA Files\Voxel\Generation SOA Files\Voxel SOA Files\Voxel SOA Files\Voxel SOA Files\Screens\Test SOA Files\Voxel\Texturing SOA Files\Voxel\Texturing SOA Files\Rendering\RenderPipelines\RenderStages SOA Files SOA Files\Console SOA Files\Rendering SOA Files\Screens\Menu SOA Files\Data\IO Utils SOA Files\Data SOA Files\Game\Universe\Generation SOA Files\Rendering SOA Files\Rendering SOA Files\Screens\Test SOA Files\Game\Universe\Generation SOA Files\Console SOA Files\Voxel\Tasking SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Game\Universe\Generation SOA Files\Game\Universe\Generation SOA Files\Game\Universe SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Voxel\Models SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Voxel\Meshing SOA Files\Voxel\Meshing SOA Files\Screens\Test SOA Files\Rendering\RenderPipelines\RenderStages SOA Files\Game SOA Files\ECS\Systems SOA Files\ECS SOA Files\ECS\ComponentBuilders SOA Files\ECS\ComponentBuilders SOA Files\ECS\Components SOA Files\Screens\Test SOA Files\Game\Universe SOA Files\Game\Universe\Generation SOA Files\Voxel\Generation SOA Files\Voxel\Access SOA Files\Console SOA Files\Voxel\Access SOA Files\ECS\Updaters\GameSystem SOA Files\ECS\Updaters\GameSystem SOA Files\Voxel\Meshing SOA Files\ECS\Updaters\GameSystem SOA Files\Game\Objects SOA Files\Game\Universe\Generation SOA Files\Game\Universe\Generation Resource Files Resource Files SOA Files\Voxel\WSO SOA Files\Voxel SOA Files\Ext\Input ================================================ FILE: SoA/SOA.vcxproj.yml ================================================ Project: DefaultTargets: Build Import: - Project: $(VCTargetsPath)\Microsoft.Cpp.Default.props - Project: $(VCTargetsPath)\Microsoft.Cpp.props - Project: $(VCTargetsPath)\Microsoft.Cpp.targets ImportGroup: - Label: ExtensionSettings - Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' Import: Condition: exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props') Label: LocalAppDataPlatform Project: $(UserRootDir)\Microsoft.Cpp.$(Platform).user.props Label: PropertySheets - Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' Import: Condition: exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props') Label: LocalAppDataPlatform Project: $(UserRootDir)\Microsoft.Cpp.$(Platform).user.props Label: PropertySheets - Label: ExtensionTargets ItemDefinitionGroup: - ClCompile: AdditionalIncludeDirectories: $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) ForcedIncludeFiles: null ForcedUsingFiles: null Optimization: Disabled PrecompiledHeader: Use PreprocessorDefinitions: WINDOWS;DEBUG;%(PreprocessorDefinitions) WarningLevel: Level3 Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' Link: AdditionalDependencies: opengl32.lib;glew32.lib;SDL2.lib;SDL2main.lib;SDL2_ttf.lib;awesomium.lib;fmod_vc.lib;zdll.lib;%(AdditionalDependencies) AdditionalLibraryDirectories: $(SolutionDir)deps\lib;%(AdditionalLibraryDirectories) GenerateDebugInformation: 'true' LargeAddressAware: 'true' SubSystem: Console - ClCompile: AdditionalIncludeDirectories: $(SolutionDir)deps\include;%(AdditionalIncludeDirectories) FavorSizeOrSpeed: Speed ForcedIncludeFiles: null ForcedUsingFiles: null FunctionLevelLinking: 'true' InlineFunctionExpansion: OnlyExplicitInline IntrinsicFunctions: 'true' MultiProcessorCompilation: 'true' Optimization: MaxSpeed PrecompiledHeader: Use PreprocessorDefinitions: WINDOWS;NDEBUG;%(PreprocessorDefinitions) WarningLevel: Level3 Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' Link: AdditionalDependencies: opengl32.lib;glew32.lib;SDL2.lib;SDL2main.lib;SDL2_ttf.lib;awesomium.lib;fmod_vc.lib;zdll.lib;%(AdditionalDependencies) AdditionalLibraryDirectories: $(SolutionDir)deps\lib;%(AdditionalLibraryDirectories) EnableCOMDATFolding: 'true' GenerateDebugInformation: 'true' LargeAddressAware: 'true' OptimizeReferences: 'true' SubSystem: Console ItemGroup: - Label: ProjectConfigurations ProjectConfiguration: - Configuration: Debug Include: Debug|Win32 Platform: Win32 - Configuration: Release Include: Release|Win32 Platform: Win32 - ClInclude: - Include: src\ChunkUpdater.h - Include: src\LoadBar.h - Include: src\LoadScreen.h - Include: src\CAEngine.h - Include: src\ChunkGenerator.h - Include: src\ChunkMesher.h - Include: src\ChunkRenderer.h - Include: src\ChunkWorker.h - Include: src\DebugRenderer.h - Include: src\DepthState.h - Include: src\FragFile.h - Include: src\IGameScreen.h - Include: src\GLEnums.h - Include: src\GLProgram.h - Include: src\GLTexture.h - Include: src\GraphicsDevice.h - Include: src\IOManager.h - Include: src\Item.h - Include: src\MainGame.h - Include: src\MaterialAtlas.h - Include: src\MaterialData.h - Include: src\MaterialStack.h - Include: src\Particle.h - Include: src\ParticleBatch.h - Include: src\ParticleEmitter.h - Include: src\ParticleEngine.h - Include: src\ParticleMesh.h - Include: src\PtrRecycler.h - Include: src\RasterizerState.h - Include: src\SamplerState.h - Include: src\ScreenList.h - Include: src\SpriteBatch.h - Include: src\SpriteFont.h - Include: src\stdafx.h - Include: src\App.h - Include: src\TextureAtlasManager.h - Include: src\VoxelEditor.h - Include: src\Thread.h - Include: src\WSO.h - Include: src\WSOAtlas.h - Include: src\WSOScanner.h - Include: src\WSOData.h - Include: src\Inputs.h - Include: src\InputManager.h - Include: src\Actor.h - Include: src\atomicops.h - Include: src\BlockData.h - Include: src\Camera.h - Include: src\Chunk.h - Include: src\ChunkManager.h - Include: src\Collision.h - Include: src\Constants.h - Include: src\Errors.h - Include: src\FileSystem.h - Include: src\FrameBuffer.h - Include: src\Frustum.h - Include: src\GameControls.h - Include: src\GameManager.h - Include: src\GameMenu.h - Include: src\global.h - Include: src\ImageLoading.h - Include: src\LoadTask.h - Include: src\PhysicsBlocks.h - Include: src\PhysicsEngine.h - Include: src\Random.h - Include: src\RenderTask.h - Include: src\RenderUtils.h - Include: src\TaskQueueManager.h - Include: src\TerrainPatch.h - Include: src\lodepng.h - Include: src\ObjectLoader.h - Include: src\OpenglManager.h - Include: src\OpenGLStructs.h - Include: src\Options.h - Include: src\Particles.h - Include: src\Planet.h - Include: src\Player.h - Include: src\readerwriterqueue.h - Include: src\Rendering.h - Include: src\shader.h - Include: src\SimplexNoise.h - Include: src\Sound.h - Include: src\TerrainGenerator.h - Include: src\Texture2d.h - Include: src\ThreadPool.h - Include: src\AwesomiumUI.h - Include: src\FloraGenerator.h - Include: src\types.h - Include: src\utils.h - Include: src\VoxelRay.h - Include: src\VRayHelper.h - Include: src\WorldEditor.h - Include: src\WorldIO.h - Include: src\WorldStructs.h - Include: src\VoxelWorld.h - Include: src\VoxDefs.h - Include: src\EditorVariables.h - Include: src\ZipFile.h - ClCompile: - Include: ..\deps\include\JSON\json_spirit_reader.cpp PrecompiledHeader: - Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' text: NotUsing - Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' text: NotUsing - Include: src\ChunkUpdater.cpp - Include: src\LoadBar.cpp - Include: src\LoadScreen.cpp - Include: src\CAEngine.cpp - Include: src\ChunkGenerator.cpp - Include: src\ChunkRenderer.cpp - Include: src\ChunkWorker.cpp - Include: src\DebugRenderer.cpp - Include: src\DepthState.cpp - Include: src\FragFile.cpp - Include: src\GLProgram.cpp - Include: src\GLTexture.cpp - Include: src\GraphicsDevice.cpp - Include: src\InputManager.cpp - Include: src\Actor.cpp - Include: src\BlockData.cpp - Include: src\Camera.cpp - Include: src\Chunk.cpp - Include: src\Inputs.cpp - Include: src\IOManager.cpp - Include: src\MainGame.cpp - Include: src\Particle.cpp - Include: src\ParticleBatch.cpp - Include: src\ParticleEmitter.cpp - Include: src\ParticleEngine.cpp - Include: src\PhysicsBlocks.cpp - Include: src\PhysicsEngine.cpp - Include: src\Random.cpp - Include: src\FloraGenerator.cpp - Include: src\RasterizerState.cpp - Include: src\RenderUtils.cpp - Include: src\SamplerState.cpp - Include: src\ScreenList.cpp - Include: src\SpriteBatch.cpp - Include: src\SpriteFont.cpp - Include: src\stdafx.cpp PrecompiledHeader: - Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' text: Create - Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' text: Create - Include: src\App.cpp - Include: src\TextureAtlasManager.cpp - Include: src\VoxelEditor.cpp - Include: src\VoxelRay.cpp - Include: src\VRayHelper.cpp - Include: src\WorldIO.cpp - Include: src\ChunkManager.cpp - Include: src\ChunkMesher.cpp - Include: src\CloseTerrainPatch.cpp - Include: src\Collision.cpp - Include: src\Errors.cpp - Include: src\FileSystem.cpp - Include: src\FrameBuffer.cpp - Include: src\Frustum.cpp - Include: src\GameControls.cpp - Include: src\GameManager.cpp - Include: src\GameMenu.cpp - Include: src\global.cpp - Include: src\ImageLoading.cpp - Include: src\TerrainPatch.cpp - Include: src\lodepng.cpp - Include: src\main.cpp - Include: src\ObjectLoader.cpp - Include: src\OpenglManager.cpp - Include: src\Options.cpp - Include: src\Planet.cpp - Include: src\Player.cpp - Include: src\Rendering.cpp - Include: src\shader.cpp - Include: src\SimplexNoise.cpp - Include: src\Sound.cpp - Include: src\TerrainGenerator.cpp - Include: src\Texture2d.cpp - Include: src\ThreadPool.cpp - Include: src\AwesomiumUI.cpp - Include: src\WorldEditor.cpp - Include: src\WorldStructs.cpp - Include: src\VoxelWorld.cpp - Include: src\WSO.cpp - Include: src\WSOAtlas.cpp - Include: src\WSOScanner.cpp - Include: src\ZipFile.cpp - ResourceCompile: Include: Resources\resources.rc - Image: Include: Resources\SOA.ico - Text: - Include: docs\FileSpecs\WSO.txt - Include: TODO.txt - None: Include: src\Chunk.inl PropertyGroup: - Label: Globals ProjectGuid: '{588491F3-69BA-40E7-8CE8-2382A64D86E3}' RootNamespace: SOA - CharacterSet: MultiByte Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' ConfigurationType: Application Label: Configuration PlatformToolset: v120 UseDebugLibraries: 'true' - CharacterSet: MultiByte Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' ConfigurationType: Application Label: Configuration PlatformToolset: v120_xp UseDebugLibraries: 'false' WholeProgramOptimization: 'true' - Label: UserMacros - Condition: '''$(Configuration)|$(Platform)''==''Release|Win32''' IncludePath: $(SolutionDir)deps\Seed_Of_Andromeda_Deps\include;$(IncludePath) LibraryPath: $(SolutionDir)deps\Seed_Of_Andromeda_Deps\lib;$(LibraryPath) - Condition: '''$(Configuration)|$(Platform)''==''Debug|Win32''' IncludePath: $(SolutionDir)deps\Seed_Of_Andromeda_Deps\include;$(IncludePath) IntDir: $(Configuration) LibraryPath: $(SolutionDir)deps\Seed_Of_Andromeda_Deps\lib;$(LibraryPath) ToolsVersion: '12.0' xmlns: http://schemas.microsoft.com/developer/msbuild/2003 ================================================ FILE: SoA/ShaderAssetLoader.cpp ================================================ #include "stdafx.h" #include "ShaderAssetLoader.h" #include #include #include // TODO(Ben): Use shader loader namespace { typedef std::pair AttributeBinding; KEG_TYPE_DECL(Anon_AttributeBinding); KEG_TYPE_DEF(Anon_AttributeBinding, AttributeBinding, kt) { using namespace keg; kt.addValue("Name", Value::value(&AttributeBinding::first)); kt.addValue("Location", Value::value(&AttributeBinding::second)); } struct ShaderAssetInformation { public: nString vertexFile; nString fragmentFile; Array binds; Array frags; Array defines; }; KEG_TYPE_DECL(Anon_ShaderAssetInformation); KEG_TYPE_DEF(Anon_ShaderAssetInformation, ShaderAssetInformation, kt) { using namespace keg; kt.addValue("Vertex", Value::value(&ShaderAssetInformation::vertexFile)); kt.addValue("Fragment", Value::value(&ShaderAssetInformation::fragmentFile)); kt.addValue("Attributes", Value::array(offsetof(ShaderAssetInformation, binds), Value::custom(0, "Anon_AttributeBinding"))); kt.addValue("Fragments", Value::array(offsetof(ShaderAssetInformation, frags), Value::custom(0, "Anon_AttributeBinding"))); kt.addValue("Defines", Value::array(offsetof(ShaderAssetInformation, defines), BasicType::STRING)); } void initProgram(Sender, void* ptr) { auto& program = ((ShaderAsset*)ptr)->program; program.init(); } void finishProgram(Sender, void* ptr) { auto& program = ((ShaderAsset*)ptr)->program; program.link(); program.initAttributes(); program.initUniforms(); } typedef std::tuple&> CompileArgs; void compileShader(Sender, void* ptr) { auto& args = *(CompileArgs*)ptr; vg::ShaderSource source; source.stage = std::get<1>(args); // Add macros Array& defines = std::get<3>(args); char buf[1024]; char* dest = buf; for (size_t i = 0; i < defines.size(); i++) { source.sources.push_back(dest); dest += sprintf(dest, "#define %s\n", defines[i].c_str()); } // Add shader code source.sources.push_back(std::get<2>(args)); // Compile std::get<0>(args)->program.addShader(source); } typedef std::tuple BindArgs; void bindAttributes(Sender, void* ptr) { auto& args = *(BindArgs*)ptr; auto& binds = std::get<1>(args)->binds; vg::GLProgram& program = std::get<0>(args)->program; for (size_t i = 0; i < binds.size(); i++) { program.setAttribute(binds[i].first, binds[i].second); } } void bindFragments(Sender, void* ptr) { auto& args = *(BindArgs*)ptr; auto& binds = std::get<1>(args)->binds; vg::GLProgram& program = std::get<0>(args)->program; for (size_t i = 0; i < binds.size(); i++) { program.bindFragDataLocation(binds[i].second, binds[i].first.c_str()); } } } void vcore::AssetBuilder::create(const vpath& p, OUT ShaderAsset* asset, vcore::RPCManager& rpc) { GLRPC so(asset); vio::IOManager iom; // Read the YAML information ShaderAssetInformation info{}; const cString str = iom.readFileToString(p); keg::parse(&info, str, "Anon_ShaderAssetInformation"); delete[] str; // Set the root directory to the YAML info file vpath root = p; root--; iom.setSearchDirectory(root); // Initialize the program so.set([](Sender s, void* d) { initProgram(s, d); }); rpc.invoke(&so); auto func1=[=](Sender, const nString& msg) { printf("PROG COMP ERROR:\n%s\n", msg.c_str()); }; auto d1 = makeDelegate(&func1); auto func2=[=](Sender, const nString& msg) { printf("PROG LINK ERROR:\n%s\n", msg.c_str()); }; auto d2 = makeDelegate(&func2); asset->program.onShaderCompilationError += d1; asset->program.onProgramLinkError += d2; { // Read vertex file str = iom.readFileToString(info.vertexFile); CompileArgs args(asset, vg::ShaderType::VERTEX_SHADER, str, info.defines); so.data.userData = &args; so.set([](Sender s, void* d) { compileShader(s, d); }); rpc.invoke(&so); delete[] str; } { // Read fragment file str = iom.readFileToString(info.fragmentFile); CompileArgs args(asset, vg::ShaderType::FRAGMENT_SHADER, str, info.defines); so.data.userData = &args; so.set([](Sender s, void* d) { compileShader(s, d); }); rpc.invoke(&so); delete[] str; } { // Bind all attribute locations BindArgs args(asset, &info); so.data.userData = &args; so.set([](Sender s, void* d) { bindAttributes(s, d); }); rpc.invoke(&so); } if (info.frags.size() > 0) { // Bind all fragment locations BindArgs args(asset, &info); so.data.userData = &args; so.set([](Sender s, void* d) { bindFragments(s, d); }); rpc.invoke(&so); } // Link the program so.data.userData = asset; so.set([](Sender s, void* d) { finishProgram(s, d); }); rpc.invoke(&so); asset->program.onShaderCompilationError -= d1; asset->program.onProgramLinkError -= d2; } void vcore::AssetBuilder::destroy(ShaderAsset* asset) { asset->program.dispose(); } ================================================ FILE: SoA/ShaderAssetLoader.h ================================================ /// /// ShaderAssetLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 4 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles loading shader assets /// #pragma once #ifndef ShaderAssetLoader_h__ #define ShaderAssetLoader_h__ #include #include class ShaderAsset : public vcore::Asset { public: vg::GLProgram program; }; template<> struct vcore::AssetBuilder { public: void create(const vpath& p, OUT ShaderAsset* asset, vcore::RPCManager& rpc); void destroy(ShaderAsset* asset); }; CONTEXTUAL_ASSET_LOADER(ShaderAssetLoader, ShaderAsset); #endif // ShaderAssetLoader_h__ ================================================ FILE: SoA/ShaderLoader.cpp ================================================ #include "stdafx.h" #include "ShaderLoader.h" #include #include namespace { void printShaderError(Sender s VORB_MAYBE_UNUSED, const nString& n) { puts("Shader Error: "); puts(n.c_str()); } void printLinkError(Sender s VORB_MAYBE_UNUSED, const nString& n) { puts("Link Error: "); puts(n.c_str()); } void printFileIOError(Sender s VORB_MAYBE_UNUSED, const nString& n) { puts("FIle IO Error: "); puts(n.c_str()); } } CALLER_DELETE vg::GLProgram ShaderLoader::createProgramFromFile(const vio::Path& vertPath, const vio::Path& fragPath, vio::IOManager* iom /*= nullptr*/, const cString defines /*= nullptr*/) { vg::ShaderManager::onFileIOFailure += makeDelegate(printFileIOError); vg::ShaderManager::onShaderCompilationError += makeDelegate(printShaderError); vg::ShaderManager::onProgramLinkError += makeDelegate(printLinkError); vg::GLProgram program; while (true) { program = vg::ShaderManager::createProgramFromFile(vertPath, fragPath, iom, defines); if (program.isLinked()) break; program.dispose(); printf("Enter any key to try recompiling with Vertex Shader: %s and Fragment Shader %s\nEnter Z to abort.\n", vertPath.getCString(), fragPath.getCString()); char tmp; std::cin >> tmp; if (tmp == 'Z' || tmp == 'z') break; } vg::ShaderManager::onFileIOFailure -= makeDelegate(printFileIOError); vg::ShaderManager::onShaderCompilationError -= makeDelegate(printShaderError); vg::ShaderManager::onProgramLinkError -= makeDelegate(printLinkError); return program; } CALLER_DELETE vg::GLProgram ShaderLoader::createProgram(const cString displayName, const cString vertSrc, const cString fragSrc, vio::IOManager* iom /*= nullptr*/, const cString defines /*= nullptr*/) { vg::ShaderManager::onFileIOFailure += makeDelegate(printFileIOError); vg::ShaderManager::onShaderCompilationError += makeDelegate(printShaderError); vg::ShaderManager::onProgramLinkError += makeDelegate(printLinkError); vg::GLProgram program; while (true) { program = vg::ShaderManager::createProgram(vertSrc, fragSrc, iom, iom, defines); if (program.isLinked()) break; program.dispose(); printf("Enter any key to try recompiling with %s shader.\nEnter Z to abort.\n", displayName); char tmp; std::cin >> tmp; if (tmp == 'Z' || tmp == 'z') break; } vg::ShaderManager::onFileIOFailure -= makeDelegate(printFileIOError); vg::ShaderManager::onShaderCompilationError -= makeDelegate(printShaderError); vg::ShaderManager::onProgramLinkError -= makeDelegate(printLinkError); return program; } ================================================ FILE: SoA/ShaderLoader.h ================================================ /// /// ShaderLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 31 Mar 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles easy loading and error checking of shaders for SoA /// #pragma once #ifndef ShaderLoader_h__ #define ShaderLoader_h__ #include #include #include DECL_VIO(class IOManager); #pragma once class ShaderLoader { public: /// Creates a program using code loaded from files, and does error checking /// Does not register with global cache static CALLER_DELETE vg::GLProgram createProgramFromFile(const vio::Path& vertPath, const vio::Path& fragPath, vio::IOManager* iom = nullptr, const cString defines = nullptr); /// Creates a program using passed code, and does error checking /// Does not register with global cache static CALLER_DELETE vg::GLProgram createProgram(const cString displayName, const cString vertSrc, const cString fragSrc, vio::IOManager* iom = nullptr, const cString defines = nullptr); }; #endif // ShaderLoader_h__ ================================================ FILE: SoA/SkyboxRenderStage.cpp ================================================ #include "stdafx.h" #include "SkyboxRenderStage.h" #include #include #include #include "Camera.h" #include "Errors.h" #include "LoadContext.h" #include "ModPathResolver.h" #include "ShaderLoader.h" #include "SkyboxRenderer.h" #include "SoAState.h" #include const ui32 TASK_WORK = 4; const ui32 TOTAL_TASKS = 8; const ui32 TOTAL_WORK = TOTAL_TASKS * TASK_WORK; void SkyboxRenderStage::init(vui::GameWindow* window, StaticLoadContext& context) { IRenderStage::init(window, context); context.addAnticipatedWork(TOTAL_WORK, TOTAL_TASKS); } void SkyboxRenderStage::hook(SoaState* state) { m_textureResolver = &state->clientState.texturePathResolver; } void SkyboxRenderStage::load(StaticLoadContext& context) { // Create texture array context.addTask([&](Sender, void*) { glGenTextures(1, &m_skyboxTextureArray); m_skyboxRenderer.initGL(); context.addWorkCompleted(TASK_WORK); checkGlError("SkyboxRenderStage inittt"); }, false); // Front (also allocates storage) context.addTask([&](Sender, void*) { vio::Path path; m_textureResolver->resolvePath("Sky/Skybox/front.png", path); vg::ScopedBitmapResource frontRes(vg::ImageIO().load(path)); m_resolution = frontRes.width; if (frontRes.height != m_resolution) { pError("Skybox textures must have equal width and height!"); } if (frontRes.data == nullptr) pError("Failed to load Sky/Skybox/front.png"); glBindTexture(GL_TEXTURE_2D_ARRAY, m_skyboxTextureArray); // Calculate max mipmap level int maxMipLevel = 0; int width = m_resolution; while (width > 1) { width >>= 1; maxMipLevel++; } // Set up all the storage width = m_resolution; for (i32 i = 0; i < maxMipLevel; i++) { glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA8, width, width, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); width >>= 1; if (width < 1) width = 1; } // Set mipmap parameters glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LOD, maxMipLevel); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, maxMipLevel); // Upload data glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, frontRes.width, frontRes.height, 1, GL_RGBA, GL_UNSIGNED_BYTE, frontRes.data); context.addWorkCompleted(TASK_WORK); checkGlError("SkyboxRenderStage waaaa"); }, false); // Right context.addTask([&](Sender, void*) { loadTexture("Sky/Skybox/right.png", 1); context.addWorkCompleted(TASK_WORK); }, false); // Top context.addTask([&](Sender, void*) { loadTexture("Sky/Skybox/top.png", 2); context.addWorkCompleted(TASK_WORK); }, false); // Left context.addTask([&](Sender, void*) { loadTexture("Sky/Skybox/left.png", 3); context.addWorkCompleted(TASK_WORK); }, false); // Bottom context.addTask([&](Sender, void*) { loadTexture("Sky/Skybox/bottom.png", 4); context.addWorkCompleted(TASK_WORK); }, false); // Back context.addTask([&](Sender, void*) { loadTexture("Sky/Skybox/back.png", 5); context.addWorkCompleted(TASK_WORK); }, false); // Tex parameters and mipmaps context.addTask([&](Sender, void*) { glBindTexture(GL_TEXTURE_2D_ARRAY, m_skyboxTextureArray); // Set up tex parameters glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D_ARRAY); // Unbind glBindTexture(GL_TEXTURE_2D_ARRAY, 0); // Check if we had any errors checkGlError("SkyboxRenderStage mipmaps"); context.addWorkCompleted(TASK_WORK); }, false); } void SkyboxRenderStage::render(const Camera* camera) { // Check if FOV or Aspect Ratio changed if (m_fieldOfView != camera->getFieldOfView() || m_aspectRatio != camera->getAspectRatio()) { updateProjectionMatrix(camera); } // Draw using custom proj and camera view drawSpace(m_projectionMatrix * camera->getViewMatrix()); } void SkyboxRenderStage::drawSpace(const f32m4 &VP) { vg::DepthState::NONE.set(); m_skyboxRenderer.drawSkybox(VP, m_skyboxTextureArray); vg::DepthState::FULL.set(); } void SkyboxRenderStage::updateProjectionMatrix(const Camera* camera) { // Set the camera clipping plane for rendering the skybox and set the projection matrix // The clipping dimensions don't matter so long as the skybox fits inside them #define SKYBOX_ZNEAR 0.01f #define SKYBOX_ZFAR 300.0f m_fieldOfView = camera->getFieldOfView(); m_aspectRatio = camera->getAspectRatio(); // Set up projection matrix m_projectionMatrix = glm::perspective(m_fieldOfView, m_aspectRatio, SKYBOX_ZNEAR, SKYBOX_ZFAR); } void SkyboxRenderStage::loadTexture(const char* relPath, int index) { vio::Path path; m_textureResolver->resolvePath(relPath, path); vg::ScopedBitmapResource res(vg::ImageIO().load(path)); if (res.height != m_resolution || res.width != m_resolution) { pError("Skybox textures must all have equal width and height!"); } if (res.data == nullptr) pError("Failed to load " + nString(relPath)); glBindTexture(GL_TEXTURE_2D_ARRAY, m_skyboxTextureArray); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, index, res.width, res.height, 1, GL_RGBA, GL_UNSIGNED_BYTE, res.data); checkGlError("SkyboxRenderStage::load() " + nString(relPath)); } ================================================ FILE: SoA/SkyboxRenderStage.h ================================================ /// /// SkyboxRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides the implementation for the skybox render stage /// #pragma once #ifndef SkyboxRenderStage_h__ #define SkyboxRenderStage_h__ #include "SkyboxRenderer.h" #include "IRenderStage.h" #include #include class Camera; class ModPathResolver; class SkyboxRenderStage : public IRenderStage { public: void init(vui::GameWindow* window, StaticLoadContext& context) override; void hook(SoaState* state); void load(StaticLoadContext& context) override; // Draws the render stage virtual void render(const Camera* camera) override; private: void drawSpace(const f32m4 &VP); // Update projection matrix void updateProjectionMatrix(const Camera* camera); void loadTexture(const char* relPath, int index); SkyboxRenderer m_skyboxRenderer; ///< Renders the skybox // vg::GLProgram* m_program = nullptr; ///< Program used for rendering f32m4 m_projectionMatrix; ///< Projection matrix for the skybox float m_fieldOfView; ///< Current field of view for the camera float m_aspectRatio; ///< Current aspect ratio for the camera VGTexture m_skyboxTextureArray = 0; ///< Texture array for skybox const ModPathResolver* m_textureResolver = nullptr; vcore::GLRPC m_rpc; ui32 m_resolution; // For parallel loading }; #endif // SkyboxRenderStage_h__ ================================================ FILE: SoA/SkyboxRenderer.cpp ================================================ #include "stdafx.h" #include "SkyboxRenderer.h" #include "LoadContext.h" #include #include #include #include "ShaderLoader.h" // Skybox Cube // // v6----- v5 // /| /| // v1------v0| // | | | | // | |v7---|-|v4 // |/ |/ // v2------v3 #define INDICES_PER_QUAD 6 #define VERTS_PER_QUAD 4 #define SKYBOX_FACES 6 const float skyboxSize = 10.0f; const float skyboxVertices[72] = { -skyboxSize, skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, skyboxSize, -skyboxSize, skyboxSize, skyboxSize, skyboxSize, skyboxSize, // v1-v2-v3-v0 (front) skyboxSize, skyboxSize, skyboxSize, skyboxSize, -skyboxSize, skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, skyboxSize, -skyboxSize, // v0-v3-v4-v5 (right) -skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, skyboxSize, skyboxSize, skyboxSize, skyboxSize, skyboxSize, skyboxSize, -skyboxSize, // v6-v1-v0-v5 (top) -skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, -skyboxSize, skyboxSize, skyboxSize, // v6-v7-v2-v1 (left) -skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, -skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, // v7-v4-v3-v2 (bottom) skyboxSize, skyboxSize, -skyboxSize, skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, -skyboxSize, skyboxSize, -skyboxSize }; // v5-v4-v7-v6 (back) // These are the X,Y components. The Z component is determined by integer division const float skyboxUVs[48] = { 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, // v1-v2-v3-v0 (front) 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, // v0-v3-v4-v5 (right) 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,// v6-v1-v0-v5 (top) 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,// v6-v7-v2-v1 (left) 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, // v7-v4-v3-v2 (bottom) 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }; // v5-v4-v7-v6 (back) namespace { const cString VERT_SRC = R"( uniform mat4 unWVP; in vec4 vPosition; in vec3 vUVW; out vec3 fUVW; void main() { fUVW = vUVW; gl_Position = unWVP * vPosition; } )"; const cString FRAG_SRC = R"( uniform sampler2DArray unTex; in vec3 fUVW; out vec4 pColor; void main() { pColor = texture(unTex, vec3(fUVW.xyz)); pColor.a = 1.0; })"; } SkyboxRenderer::SkyboxRenderer() { // Empty } SkyboxRenderer::~SkyboxRenderer() { destroy(); } void SkyboxRenderer::initGL() { initShader(); initBuffers(); } void SkyboxRenderer::drawSkybox(const f32m4& VP, VGTexture textureArray) { // Bind shader m_program.use(); // Bind our texture in Texture Unit 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray); // Upload VP matrix glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, GL_FALSE, &VP[0][0]); // Create the buffer objects if they aren't initialized if (m_vbo == 0) { initBuffers(); } glBindVertexArray(m_vao); glDrawElements(GL_TRIANGLES, INDICES_PER_QUAD * 6, GL_UNSIGNED_SHORT, (void*)0); //offset glBindVertexArray(0); // Unbind shader m_program.unuse(); } void SkyboxRenderer::destroy() { if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; } if (m_vbo) { vg::GpuMemory::freeBuffer(m_vbo); m_vbo = 0; } if (m_ibo) { vg::GpuMemory::freeBuffer(m_ibo); m_ibo = 0; } if (m_program.isCreated()) m_program.dispose(); } void SkyboxRenderer::initShader() { m_program = ShaderLoader::createProgram("Skybox", VERT_SRC, FRAG_SRC); // Constant uniforms m_program.use(); glUniform1i(m_program.getUniform("unTex"), 0); m_program.unuse(); } void SkyboxRenderer::initBuffers() { // Vertex Array Object glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); // Vertex Buffer Object vg::GpuMemory::createBuffer(m_vbo); vg::GpuMemory::bindBuffer(m_vbo, vg::BufferTarget::ARRAY_BUFFER); SkyboxVertex verts[VERTS_PER_QUAD * SKYBOX_FACES]; for (int i = 0; i < VERTS_PER_QUAD * SKYBOX_FACES; i++) { verts[i].position = f32v3(skyboxVertices[i * 3], skyboxVertices[i * 3 + 1], skyboxVertices[i * 3 + 2]); verts[i].texCoords = f32v3(skyboxUVs[i * 2], skyboxUVs[i * 2 + 1], (f32)(i / VERTS_PER_QUAD)); } vg::GpuMemory::uploadBufferData(m_vbo, vg::BufferTarget::ARRAY_BUFFER, sizeof(verts), verts, vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::createBuffer(m_ibo); vg::GpuMemory::bindBuffer(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); // Index buffer Object ui16 skyboxIndices[INDICES_PER_QUAD * SKYBOX_FACES]; // Set up indices i32 ci = 0; for (i32 i = 0; i < INDICES_PER_QUAD * SKYBOX_FACES; i += INDICES_PER_QUAD, ci += VERTS_PER_QUAD) { skyboxIndices[i] = ci; skyboxIndices[i + 1] = ci + 3; skyboxIndices[i + 2] = ci + 2; skyboxIndices[i + 3] = ci + 2; skyboxIndices[i + 4] = ci + 1; skyboxIndices[i + 5] = ci; } vg::GpuMemory::uploadBufferData(m_ibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, sizeof(skyboxIndices), skyboxIndices, vg::BufferUsageHint::STATIC_DRAW); // Set up attribute pointers m_program.enableVertexAttribArrays(); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, GL_FALSE, sizeof(SkyboxVertex), offsetptr(SkyboxVertex, position)); glVertexAttribPointer(m_program.getAttribute("vUVW"), 3, GL_FLOAT, GL_FALSE, sizeof(SkyboxVertex), offsetptr(SkyboxVertex, texCoords)); glBindVertexArray(0); } ================================================ FILE: SoA/SkyboxRenderer.h ================================================ /// /// SkyboxRenderer.h /// Seed of Andromeda /// /// Created by Ben Arnold on 28 Oct 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides a class to render a skybox /// #pragma once #ifndef SkyboxRenderer_h__ #define SkyboxRenderer_h__ #include class SkyboxVertex { public: f32v3 position; f32v3 texCoords; }; class LoadContext; class SkyboxRenderer { public: SkyboxRenderer(); ~SkyboxRenderer(); void initGL(); /// Draw the skybox void drawSkybox(const f32m4& VP, VGTexture textureArray); /// Frees the skybox mesh and shader void destroy(); private: /// Initializes the _vbo and _ibo buffers void initShader(); void initBuffers(); ui32 m_vao = 0; ui32 m_vbo = 0; ui32 m_ibo = 0; vg::GLProgram m_program; }; #endif // SkyboxRenderer_h__ ================================================ FILE: SoA/SmartVoxelContainer.hpp ================================================ // // SmartVoxelContainer.h // Vorb Engine // // Created by Benjamin Arnold on 14 Nov 2014 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef SmartVoxelContainer_h__ #define SmartVoxelContainer_h__ #include #include "Constants.h" #include #include #define QUIET_FRAMES_UNTIL_COMPRESS 60 #define ACCESS_COUNT_UNTIL_DECOMPRESS 5 // TODO(Cristian): We'll see how to fit it into Vorb namespace vorb { namespace voxel { static ui32 MAX_COMPRESSIONS_PER_FRAME = UINT_MAX; ///< You can optionally set this in order to limit changes per frame static ui32 totalContainerCompressions = 1; ///< Set this to 1 each frame template class SmartVoxelContainer; template class SmartHandle { friend class SmartVoxelContainer; public: operator const T&() const; SmartHandle& operator= (T data); SmartHandle& operator= (const SmartHandle& o); SmartHandle(SmartHandle&& o) : m_container(o.m_container), m_index(o.m_index) { // TODO: Empty for now try to null it out } SmartHandle(const SmartHandle& o) = delete; SmartHandle& operator= (SmartHandle&& o) = delete; private: SmartHandle(SmartVoxelContainer& container, size_t index) : m_container(container), m_index(index) { // Empty } SmartVoxelContainer& m_container; ///< The parent container that created the handle size_t m_index; ///< The index of this handle into the smart container }; /// This should be called once per frame to reset totalContainerChanges inline void clearContainerCompressionsCounter() { totalContainerCompressions = 1; ///< Start at 1 so that integer overflow handles the default case } enum class VoxelStorageState { FLAT_ARRAY = 0, INTERVAL_TREE = 1 }; template class SmartVoxelContainer { friend class SmartHandle; public: /// Constructor SmartVoxelContainer() { // Empty } /*! @brief Construct the container with a provided recycler. * * @param arrayRecycler: The recycler to be used in place of the default generated recycler. */ SmartVoxelContainer(vcore::FixedSizeArrayRecycler* arrayRecycler) { setArrayRecycler(arrayRecycler); } /*! @brief Change the array recycler. * * @param arrayRecycler: The recycler to be used in place of the default generated recycler. */ void setArrayRecycler(vcore::FixedSizeArrayRecycler* arrayRecycler) { _arrayRecycler = arrayRecycler; } SmartHandle operator[] (size_t index) { return std::move(SmartHandle(*this, index)); } const T& operator[] (size_t index) const { return (getters[(size_t)_state])(this, index); } /// Initializes the container inline void init(VoxelStorageState state) { _state = state; if (_state == VoxelStorageState::FLAT_ARRAY) { _dataArray = _arrayRecycler->create(); } } /// Creates the tree using a sorted array of data. /// The number of voxels should add up to CHUNK_SIZE /// @param state: Initial state of the container /// @param data: The sorted array used to populate the container inline void initFromSortedArray(VoxelStorageState state, const std::vector ::LNode>& data) { _state = state; _accessCount = 0; _quietFrames = 0; if (_state == VoxelStorageState::INTERVAL_TREE) { _dataTree.initFromSortedArray(data); _dataTree.checkTreeValidity(); } else { _dataArray = _arrayRecycler->create(); int index = 0; for (size_t i = 0; i < data.size(); i++) { for (int j = 0; j < data[i].length; j++) { _dataArray[index++] = data[i].data; } } } } inline void initFromSortedArray(VoxelStorageState state, const typename IntervalTree::LNode data[], size_t size) { _state = state; _accessCount = 0; _quietFrames = 0; if (_state == VoxelStorageState::INTERVAL_TREE) { _dataTree.initFromSortedArray(data, size); _dataTree.checkTreeValidity(); } else { _dataArray = _arrayRecycler->create(); int index = 0; for (size_t i = 0; i < size; i++) { for (int j = 0; j < data[i].length; j++) { _dataArray[index++] = data[i].data; } } } } inline void changeState(VoxelStorageState newState, std::mutex& dataLock) { if (newState == _state) return; if (newState == VoxelStorageState::INTERVAL_TREE) { compress(dataLock); } else { uncompress(dataLock); } _quietFrames = 0; _accessCount = 0; } /// Updates the container. Call once per frame /// @param dataLock: The mutex that guards the data inline void update(std::mutex& dataLock) { // If access count is higher than the threshold, this is not a quiet frame if (_accessCount >= ACCESS_COUNT_UNTIL_DECOMPRESS) { _quietFrames = 0; } else { _quietFrames++; } if (_state == VoxelStorageState::INTERVAL_TREE) { // Check if we should uncompress the data if (_quietFrames == 0) { uncompress(dataLock); } } else { // Check if we should compress the data if (_quietFrames >= QUIET_FRAMES_UNTIL_COMPRESS && totalContainerCompressions <= MAX_COMPRESSIONS_PER_FRAME) { compress(dataLock); } } _accessCount = 0; } /// Clears the container and frees memory inline void clear() { _accessCount = 0; _quietFrames = 0; if (_state == VoxelStorageState::INTERVAL_TREE) { _dataTree.clear(); } else if (_dataArray) { _arrayRecycler->recycle(_dataArray); _dataArray = nullptr; } } /// Uncompressed the interval tree into a buffer. /// May only be called when getState() == VoxelStorageState::INTERVAL_TREE /// or you will get a null access violation. /// @param buffer: Buffer of memory to store the result inline void uncompressIntoBuffer(T* buffer) { _dataTree.uncompressIntoBuffer(buffer); } /// Getters const VoxelStorageState& getState() const { return _state; } T* getDataArray() { return _dataArray; } const T* getDataArray() const { return _dataArray; } IntervalTree& getTree() { return _dataTree; } const IntervalTree& getTree() const { return _dataTree; } /// Gets the element at index /// @param index: must be (0, SIZE] /// @return The element inline const T& get(size_t index) const { return (getters[(size_t)_state])(this, index); } /// Sets the element at index /// @param index: must be (0, SIZE] /// @param value: The value to set at index inline void set(size_t index, T value) { _accessCount++; (setters[(size_t)_state])(this, index, value); } private: typedef const T& (*Getter)(const SmartVoxelContainer*, size_t); typedef void(*Setter)(SmartVoxelContainer*, size_t, T); static const T& getInterval(const SmartVoxelContainer* container, size_t index) { return container->_dataTree.getData(index); } static const T& getFlat(const SmartVoxelContainer* container, size_t index) { return container->_dataArray[index]; } static void setInterval(SmartVoxelContainer* container, size_t index, T data) { container->_dataTree.insert(index, data); } static void setFlat(SmartVoxelContainer* container, size_t index, T data) { container->_dataArray[index] = data; } static Getter getters[2]; static Setter setters[2]; inline void uncompress(std::mutex& dataLock) { dataLock.lock(); _dataArray = _arrayRecycler->create(); uncompressIntoBuffer(_dataArray); // Free memory _dataTree.clear(); // Set the new state _state = VoxelStorageState::FLAT_ARRAY; dataLock.unlock(); } inline void compress(std::mutex& dataLock) { dataLock.lock(); // Sorted array for creating the interval tree // Using stack array to avoid allocations, beware stack overflow typename IntervalTree::LNode data[CHUNK_SIZE]; int index = 0; data[0].set(0, 1, _dataArray[0]); // Set the data for (int i = 1; i < CHUNK_SIZE; ++i) { if (_dataArray[i] == data[index].data) { ++(data[index].length); } else { data[++index].set(i, 1, _dataArray[i]); } } // Set new state _state = VoxelStorageState::INTERVAL_TREE; // Create the tree _dataTree.initFromSortedArray(data, index + 1); dataLock.unlock(); // Recycle memory _arrayRecycler->recycle(_dataArray); _dataArray = nullptr; totalContainerCompressions++; } IntervalTree _dataTree; ///< Interval tree of voxel data T* _dataArray = nullptr; ///< pointer to an array of voxel data int _accessCount = 0; ///< Number of times the container was accessed this frame int _quietFrames = 0; ///< Number of frames since we have had heavy updates VoxelStorageState _state = VoxelStorageState::FLAT_ARRAY; ///< Current data structure state vcore::FixedSizeArrayRecycler* _arrayRecycler = nullptr; ///< For recycling the voxel arrays }; /*template inline SmartHandle::operator const T&() const { return m_container[m_index]; }*/ template inline SmartHandle::operator const T&() const { return (m_container.getters[(size_t)m_container.getState()])(&m_container, m_index); } template inline SmartHandle& SmartHandle::operator= (T data) { m_container.set(m_index, data); return *this; } template inline SmartHandle& SmartHandle::operator= (const SmartHandle& o) { m_container.set(m_index, o.m_container[o.m_index]); return *this; } template typename SmartVoxelContainer::Getter SmartVoxelContainer::getters[2] = { SmartVoxelContainer::getFlat, SmartVoxelContainer::getInterval }; template typename SmartVoxelContainer::Setter SmartVoxelContainer::setters[2] = { SmartVoxelContainer::setFlat, SmartVoxelContainer::setInterval }; } } namespace vvox = vorb::voxel; #endif // SmartVoxelContainer_h__ ================================================ FILE: SoA/SoAState.h ================================================ /// /// SoaState.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 10 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// The main game state for SoA /// #pragma once #ifndef SoAState_h__ #define SoAState_h__ #include "SpaceSystem.h" #include "GameSystem.h" #include "BlockPack.h" #include "ChunkAllocator.h" #include "ClientState.h" #include "Item.h" #include "ECSTemplates.h" #include #include #include class PlanetGenLoader; class SoaOptions; DECL_VIO(class IOManager); struct SoaState { SoaState() {} SpaceSystem* spaceSystem = nullptr; GameSystem* gameSystem = nullptr; // TODO(Ben): Clean up this dumping ground PagedChunkAllocator chunkAllocator; ECSTemplateLibrary templateLib; // TODO(Ben): Move somewhere else. ClientState clientState; vio::IOManager* systemIoManager = nullptr; vcore::ThreadPool* threadPool = nullptr; SoaOptions* options = nullptr; // Lives in App BlockPack blocks; ItemPack items; vio::IOManager saveFileIom; f64 time = 0.0; bool isInputEnabled = true; float timeStep = 0.016f; private: VORB_NON_COPYABLE(SoaState); }; #endif // SoAState_h__ ================================================ FILE: SoA/SoaController.cpp ================================================ #include "stdafx.h" #include "SoaController.h" #include #include "App.h" #include "GameSystemAssemblages.h" #include "GameSystemUpdater.h" #include "SoAState.h" #include "SoaOptions.h" #include "SoaEngine.h" #include "OrbitComponentUpdater.h" SoaController::~SoaController() { // Empty } void initCreativeInventory(vecs::EntityID eid, SoaState* state) { auto& invCmp = state->gameSystem->inventory.getFromEntity(eid); const std::vector& blocks = state->blocks.getBlockList(); // Skip first two blocks for (size_t i = 2; i < blocks.size(); i++) { if (!state->items.hasItem(i)) { ItemData d; d.blockID = i; d.maxCount = UINT_MAX; d.name = blocks[i].name; d.type = ItemType::BLOCK; // Add new item to stack ItemStack stack; stack.id = state->items.append(d); stack.count = UINT_MAX; stack.pack = &state->items; invCmp.items.push_back(stack); } } } void SoaController::startGame(SoaState* state) { // Load game ECS SoaEngine::loadGameSystem(state); GameSystem* gameSystem = state->gameSystem; SpaceSystem* spaceSystem = state->spaceSystem; // TODO(Ben): Client only ClientState& clientState = state->clientState; if (clientState.isNewGame) { // Create the player entity and make the initial planet his parent if (clientState.startingPlanet) { clientState.playerEntity = state->templateLib.build(*gameSystem, "Player"); auto& spacePos = gameSystem->spacePosition.getFromEntity(clientState.playerEntity); spacePos.position = clientState.startSpacePos; spacePos.parentEntity = clientState.startingPlanet; spacePos.parentGravity = spaceSystem->sphericalGravity.getComponentID(clientState.startingPlanet); spacePos.parentSphericalTerrain = spaceSystem->sphericalTerrain.getComponentID(clientState.startingPlanet); auto& physics = gameSystem->physics.getFromEntity(clientState.playerEntity); physics.spacePosition = gameSystem->spacePosition.getComponentID(clientState.playerEntity); } else { clientState.playerEntity = state->templateLib.build(*gameSystem, "Player"); auto& spacePos = gameSystem->spacePosition.getFromEntity(clientState.playerEntity); spacePos.position = state->clientState.startSpacePos; } // TODO(Ben): Temporary initCreativeInventory(clientState.playerEntity, state); } else { // TODO(Ben): This } } f64v3 SoaController::getEntityEyeVoxelPosition(SoaState* state, vecs::EntityID eid) { auto& hCmp = state->gameSystem->head.getFromEntity(eid); auto& vpCmp = state->gameSystem->voxelPosition.get(hCmp.voxelPosition); return vpCmp.gridPosition.pos + hCmp.relativePosition; } f64v3 SoaController::getEntityViewVoxelDirection(SoaState* state, vecs::EntityID eid) { auto& hCmp = state->gameSystem->head.getFromEntity(eid); auto& vpCmp = state->gameSystem->voxelPosition.get(hCmp.voxelPosition); f64v3 v(0.0, 0.0, 1.0); return vpCmp.orientation * hCmp.relativeOrientation * v; } ================================================ FILE: SoA/SoaController.h ================================================ /// /// SoaController.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Main game controller for SoA /// #pragma once #ifndef SoaController_h__ #define SoaController_h__ class App; struct SoaState; #include class SoaController { public: virtual ~SoaController(); void startGame(SoaState* state); f64v3 getEntityEyeVoxelPosition(SoaState* state, vecs::EntityID eid); f64v3 getEntityViewVoxelDirection(SoaState* state, vecs::EntityID eid); }; #endif // SoaController_h__ ================================================ FILE: SoA/SoaEngine.cpp ================================================ #include "stdafx.h" #include "SoaEngine.h" #include "BlockData.h" #include "BlockPack.h" #include "ChunkMeshManager.h" #include "ChunkUpdater.h" #include "DebugRenderer.h" #include "GameSystemComponentBuilders.h" #include "PlanetGenLoader.h" #include "ProgramGenDelegate.h" #include "SoAState.h" #include "SpaceSystemAssemblages.h" #define M_PER_KM 1000.0 OptionsController SoaEngine::optionsController; void SoaEngine::initOptions(SoaOptions& options) { options.addOption(OPT_PLANET_DETAIL, "Planet Detail", OptionValue(1)); options.addOption(OPT_VOXEL_RENDER_DISTANCE, "Voxel Render Distance", OptionValue(144.0f)); options.addOption(OPT_HUD_MODE, "Hud Mode", OptionValue(0)); options.addOption(OPT_TEXTURE_RES, "Texture Resolution", OptionValue(32)); options.addOption(OPT_MOTION_BLUR, "Motion Blur", OptionValue(0)); options.addOption(OPT_DEPTH_OF_FIELD, "Depth Of Field", OptionValue(0)); options.addOption(OPT_MSAA, "MSAA", OptionValue(0)); options.addOption(OPT_MAX_MSAA, "Max MSAA", OptionValue(8)); options.addOption(OPT_SPECULAR_EXPONENT, "Specular Exponent", OptionValue(8.0f)); options.addOption(OPT_SPECULAR_INTENSITY, "Specular Intensity", OptionValue(0.3f)); options.addOption(OPT_HDR_EXPOSURE, "HDR Exposure", OptionValue(0.5f)); //TODO(Ben): Useless? options.addOption(OPT_GAMMA, "Gamma", OptionValue(1.0f)); options.addOption(OPT_SEC_COLOR_MULT, "Sec Color Mult", OptionValue(0.1f)); //TODO(Ben): Useless? options.addOption(OPT_FOV, "FOV", OptionValue(70.0f)); options.addOption(OPT_VSYNC, "VSYNC", OptionValue(true)); options.addOption(OPT_MAX_FPS, "Max FPS", OptionValue(60.0f)); //TODO(Ben): appdata.config? options.addOption(OPT_VOXEL_LOD_THRESHOLD, "Voxel LOD Threshold", OptionValue(128.0f)); options.addOption(OPT_MUSIC_VOLUME, "Music Volume", OptionValue(1.0f)); options.addOption(OPT_EFFECT_VOLUME, "Effect Volume", OptionValue(1.0f)); options.addOption(OPT_MOUSE_SENSITIVITY, "Mouse Sensitivity", OptionValue(1.0f)); options.addOption(OPT_INVERT_MOUSE, "Invert Mouse", OptionValue(false)); options.addOption(OPT_FULLSCREEN, "Fullscreen", OptionValue(false)); options.addOption(OPT_BORDERLESS, "Borderless Window", OptionValue(false)); options.addOption(OPT_SCREEN_WIDTH, "Screen Width", OptionValue(1280)); options.addOption(OPT_SCREEN_HEIGHT, "Screen Height", OptionValue(720)); options.addStringOption("Texture Pack", "Default"); SoaEngine::optionsController.setDefault(); } void SoaEngine::initState(SoaState* state) { state->gameSystem = new GameSystem; state->spaceSystem = new SpaceSystem; state->systemIoManager = new vio::IOManager; { // Threadpool init size_t hc = std::thread::hardware_concurrency(); // Remove two threads for the render thread and main thread if (hc > 1) hc--; if (hc > 1) hc--; // Drop a thread so we don't steal the hardware on debug #ifdef DEBUG if (hc > 1) hc--; #endif // Initialize the threadpool with hc threads state->threadPool = new vcore::ThreadPool(); state->threadPool->init(hc); } // Init ECS // TODO(Ben): Mod packs state->templateLib.registerFactory(GAME_SYSTEM_CT_AABBCOLLIDABLE_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_SPACEPOSITION_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_VOXELPOSITION_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_PHYSICS_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_FRUSTUM_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_HEAD_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_PARKOURINPUT_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_ATTRIBUTES_NAME); state->templateLib.registerFactory(GAME_SYSTEM_CT_INVENTORY_NAME); { // Load ECS templates vio::IOManager iom; vio::DirectoryEntries entries; iom.getDirectoryEntries("Data/ECS", entries); for (auto& p : entries) { if (p.isFile()) state->templateLib.loadTemplate(p); } } ChunkUpdater::blockPack = &state->blocks; // TODO(Ben): Move somewhere else initClientState(state, state->clientState); } void SoaEngine::initClientState(SoaState* soaState, ClientState& state) { state.debugRenderer = new DebugRenderer; state.chunkMeshManager = new ChunkMeshManager(soaState->threadPool, &soaState->blocks); state.systemViewer = new MainMenuSystemViewer; // TODO(Ben): This is also elsewhere? state.texturePathResolver.init("Textures/TexturePacks/" + soaOptions.getStringOption("Texture Pack").defaultValue + "/", "Textures/TexturePacks/" + soaOptions.getStringOption("Texture Pack").value + "/"); // TODO(Ben): Don't hardcode this. Load a texture pack file state.blockTextures = new BlockTexturePack; state.blockTextures->init(32, 4096); state.blockTextureLoader.init(&state.texturePathResolver, state.blockTextures); } bool SoaEngine::loadSpaceSystem(SoaState* state, const nString& filePath) { AutoDelegatePool pool; vpath path = "SoASpace.log"; vfile file; path.asFile(&file); vfstream fs = file.open(vio::FileOpenFlags::READ_WRITE_CREATE); pool.addAutoHook(state->spaceSystem->onEntityAdded, [=] (Sender, vecs::EntityID eid) { fs.write("Entity added: %d\n", eid); }); for (auto namedTable : state->spaceSystem->getComponents()) { auto table = state->spaceSystem->getComponentTable(namedTable.first); pool.addAutoHook(table->onEntityAdded, [=] (Sender, vecs::ComponentID cid, vecs::EntityID eid) { fs.write("Component \"%s\" added: %d -> Entity %d\n", namedTable.first.c_str(), cid, eid); }); } // Load system SpaceSystemLoader spaceSystemLoader; spaceSystemLoader.init(state); spaceSystemLoader.loadStarSystem(filePath); pool.dispose(); return true; } bool SoaEngine::loadGameSystem(SoaState* state VORB_UNUSED) { // TODO(Ben): Implement and remove VORB_UNUSED tag. return true; } #define SET_RANGE(a, b, name) a.name.min = b.name.x; a.name.max = b.name.y; #define TRY_SET_BLOCK(id, bp, name) bp = blocks.hasBlock(name); if (bp) id = bp->ID; inline void setTreeFruitProperties(TreeTypeFruitProperties& fp, const FruitKegProperties& kp, const PlanetGenData* genData) { SET_RANGE(fp, kp, chance); auto it = genData->floraMap.find(kp.flora); if (it != genData->floraMap.end()) { fp.flora = it->second; } } void setTreeLeafProperties(TreeTypeLeafProperties& lp, const LeafKegProperties& kp, const PlanetGenData* genData, const BlockPack& blocks) { lp.type = kp.type; setTreeFruitProperties(lp.fruitProps, kp.fruitProps, genData); const Block* b; switch (lp.type) { case TreeLeafType::ROUND: SET_RANGE(lp, kp, round.vRadius); SET_RANGE(lp, kp, round.hRadius); TRY_SET_BLOCK(lp.round.blockID, b, kp.block); break; case TreeLeafType::PINE: SET_RANGE(lp, kp, pine.oRadius); SET_RANGE(lp, kp, pine.iRadius); SET_RANGE(lp, kp, pine.period); TRY_SET_BLOCK(lp.pine.blockID, b, kp.block); break; case TreeLeafType::MUSHROOM: SET_RANGE(lp, kp, mushroom.tvRadius); SET_RANGE(lp, kp, mushroom.thRadius); SET_RANGE(lp, kp, mushroom.bvRadius); SET_RANGE(lp, kp, mushroom.bhRadius); SET_RANGE(lp, kp, mushroom.bLength); SET_RANGE(lp, kp, mushroom.capWidth); SET_RANGE(lp, kp, mushroom.gillWidth); lp.mushroom.interp = kp.mushroom.interp; // Block overrides cap and gill when they are none if (kp.block != "none" && kp.mushGillBlock == "none" && kp.mushCapBlock == "none") { TRY_SET_BLOCK(lp.mushroom.gillBlockID, b, kp.block); TRY_SET_BLOCK(lp.mushroom.capBlockID, b, kp.block); } else { TRY_SET_BLOCK(lp.mushroom.gillBlockID, b, kp.mushGillBlock); TRY_SET_BLOCK(lp.mushroom.capBlockID, b, kp.mushCapBlock); } break; case TreeLeafType::NONE: break; } } void setTreeBranchProperties(TreeTypeBranchProperties& bp, const BranchKegProperties& kp, const PlanetGenData* genData, const BlockPack& blocks) { SET_RANGE(bp, kp, coreWidth); SET_RANGE(bp, kp, barkWidth); SET_RANGE(bp, kp, widthFalloff); SET_RANGE(bp, kp, branchChance); SET_RANGE(bp, kp, angle); SET_RANGE(bp, kp, subBranchAngle); SET_RANGE(bp, kp, changeDirChance); const Block* b; TRY_SET_BLOCK(bp.coreBlockID, b,kp.coreBlock); TRY_SET_BLOCK(bp.barkBlockID, b, kp.barkBlock); setTreeFruitProperties(bp.fruitProps, kp.fruitProps, genData); setTreeLeafProperties(bp.leafProps, kp.leafProps, genData, blocks); } void SoaEngine::initVoxelGen(PlanetGenData* genData, const BlockPack& blocks) { PlanetBlockInitInfo& blockInfo = genData->blockInfo; if (genData) { // Set all block layers genData->blockLayers.resize(blockInfo.blockLayers.size()); for (size_t i = 0; i < blockInfo.blockLayers.size(); i++) { BlockLayerKegProperties& kp = blockInfo.blockLayers[i]; BlockLayer& l = genData->blockLayers[i]; l.width = kp.width; const Block* b = blocks.hasBlock(kp.block); if (b) { l.block = b->ID; } else { l.block = 0; } if (kp.surface.size()) { b = blocks.hasBlock(kp.surface); if (b) { l.surfaceTransform = b->ID; } else { l.surfaceTransform = l.block; } } else { l.surfaceTransform = l.block; } } // Set starts for binary search application int start = 0; for (auto& l : genData->blockLayers) { l.start = start; start += l.width; } // Set liquid block if (blockInfo.liquidBlockName.length()) { if (blocks.hasBlock(blockInfo.liquidBlockName)) { genData->liquidBlock = blocks[blockInfo.liquidBlockName].ID; } } // Set surface block if (blockInfo.surfaceBlockName.length()) { if (blocks.hasBlock(blockInfo.surfaceBlockName)) { genData->surfaceBlock = blocks[blockInfo.surfaceBlockName].ID; } } // Set flora data genData->flora.resize(blockInfo.flora.size()); for (size_t i = 0; i < blockInfo.flora.size(); i++) { FloraKegProperties& kp = blockInfo.flora[i]; const Block* b = blocks.hasBlock(kp.block); if (b) { FloraType& ft = genData->flora[i]; genData->floraMap[kp.id] = i; ft.block = b->ID; ft.height.min = kp.height.x; ft.height.max = kp.height.y; ft.slope.min = kp.slope.x; ft.slope.max = kp.slope.y; ft.dSlope.min = kp.dSlope.x; ft.dSlope.max = kp.dSlope.y; ft.dir = kp.dir; } } // Set sub-flora for (size_t i = 0; i < genData->flora.size(); i++) { FloraKegProperties& kp = blockInfo.flora[i]; FloraType& ft = genData->flora[i]; if (kp.nextFlora.size()) { auto it = genData->floraMap.find(kp.nextFlora); if (it != genData->floraMap.end()) { ft.nextFlora = &genData->flora[it->second]; } else { ft.nextFlora = nullptr; } } else { ft.nextFlora = nullptr; } } // Set tree types genData->trees.resize(blockInfo.trees.size()); for (size_t i = 0; i < blockInfo.trees.size(); ++i) { NTreeType& td = genData->trees[i]; const TreeKegProperties& kp = blockInfo.trees[i]; // Add to lookup map genData->treeMap[kp.id] = i; // Set height range SET_RANGE(td, kp, height); SET_RANGE(td, kp, branchPoints); SET_RANGE(td, kp, branchStep); SET_RANGE(td, kp, killMult); SET_RANGE(td, kp, infRadius); // Set branch volume properties td.branchVolumes.resize(kp.branchVolumes.size()); for (size_t j = 0; j < kp.branchVolumes.size(); j++) { TreeTypeBranchVolumeProperties& tp = td.branchVolumes[j]; const BranchVolumeKegProperties& tkp = kp.branchVolumes[j]; SET_RANGE(tp, tkp, height); SET_RANGE(tp, tkp, hRadius); SET_RANGE(tp, tkp, vRadius); SET_RANGE(tp, tkp, points); } // Set trunk properties td.trunkProps.resize(kp.trunkProps.size()); for (size_t j = 0; j < kp.trunkProps.size(); j++) { TreeTypeTrunkProperties& tp = td.trunkProps[j]; const TrunkKegProperties& tkp = kp.trunkProps[j]; const Block* b; tp.loc = tkp.loc; TRY_SET_BLOCK(tp.barkBlockID, b, tkp.barkBlock); TRY_SET_BLOCK(tp.coreBlockID, b, tkp.coreBlock); // Set ranges SET_RANGE(tp, tkp, coreWidth); SET_RANGE(tp, tkp, barkWidth); SET_RANGE(tp, tkp, branchChance); SET_RANGE(tp, tkp, changeDirChance); tp.slope.min.min = tkp.slope[0].x; tp.slope.min.max = tkp.slope[0].y; tp.slope.max.min = tkp.slope[1].x; tp.slope.max.max = tkp.slope[1].y; tp.interp = tkp.interp; setTreeFruitProperties(tp.fruitProps, tkp.fruitProps, genData); setTreeBranchProperties(tp.branchProps, tkp.branchProps, genData, blocks); setTreeLeafProperties(tp.leafProps, tkp.leafProps, genData, blocks); } } // Set biome trees and flora for (auto& biome : genData->biomes) { biome.genData = genData; { // Flora auto it = blockInfo.biomeFlora.find(&biome); if (it != blockInfo.biomeFlora.end()) { auto& kList = it->second; biome.flora.resize(kList.size()); // Iterate through keg properties for (size_t i = 0; i < kList.size(); i++) { auto& kp = kList[i]; auto mit = genData->floraMap.find(kp.id); if (mit != genData->floraMap.end()) { biome.flora[i].chance = kp.chance; biome.flora[i].data = &genData->flora[mit->second]; biome.flora[i].id = i; } else { fprintf(stderr, "Failed to find flora id %s", kp.id.c_str()); } } } } { // Trees auto it = blockInfo.biomeTrees.find(&biome); if (it != blockInfo.biomeTrees.end()) { auto& kList = it->second; biome.trees.resize(kList.size()); // Iterate through keg properties for (size_t i = 0; i < kList.size(); i++) { auto& kp = kList[i]; auto mit = genData->treeMap.find(kp.id); if (mit != genData->treeMap.end()) { biome.trees[i].chance = kp.chance; biome.trees[i].data = &genData->trees[mit->second]; // Trees and flora share IDs so we can use a single // value in heightmap. Thus, add flora.size(). biome.trees[i].id = biome.flora.size() + i; } else { fprintf(stderr, "Failed to find flora id %s", kp.id.c_str()); } } } } } // Clear memory blockInfo = PlanetBlockInitInfo(); } } #undef SET_RANGE // TODO: Investigate why glRPC isn't currently being used. void SoaEngine::reloadSpaceBody(SoaState* state, vecs::EntityID eid, vcore::RPCManager* glRPC VORB_UNUSED) { SpaceSystem* spaceSystem = state->spaceSystem; auto& stCmp = spaceSystem->sphericalTerrain.getFromEntity(eid); f64 radius = stCmp.radius; auto npCmpID = stCmp.namePositionComponent; auto arCmpID = stCmp.axisRotationComponent; auto ftCmpID = stCmp.farTerrainComponent; WorldCubeFace face = WorldCubeFace::FACE_NONE; PlanetGenData* genData = stCmp.planetGenData; nString filePath = genData->terrainFilePath; if (ftCmpID) { face = spaceSystem->farTerrain.getFromEntity(eid).face; SpaceSystemAssemblages::removeFarTerrainComponent(spaceSystem, eid); } if (stCmp.sphericalVoxelComponent) { SpaceSystemAssemblages::removeSphericalVoxelComponent(spaceSystem, eid); } SpaceSystemAssemblages::removeSphericalTerrainComponent(spaceSystem, eid); //state->planetLoader->textureCache.freeTexture(genData->liquidColorMap); // state->planetLoader->textureCache.freeTexture(genData->terrainColorMap); PlanetGenLoader loader; loader.init(state->systemIoManager); genData = loader.loadPlanetGenData(filePath); genData->radius = radius; auto stCmpID = SpaceSystemAssemblages::addSphericalTerrainComponent(spaceSystem, eid, npCmpID, arCmpID, radius, genData, state->threadPool); if (ftCmpID) { auto ftCmpID = SpaceSystemAssemblages::addFarTerrainComponent(spaceSystem, eid, stCmp, face); stCmp.farTerrainComponent = ftCmpID; } // TODO(Ben): this doesn't work too well. auto& pCmp = state->gameSystem->spacePosition.getFromEntity(state->clientState.playerEntity); pCmp.parentSphericalTerrain = stCmpID; pCmp.parentGravity = spaceSystem->sphericalGravity.getComponentID(eid); pCmp.parentEntity = eid; } void SoaEngine::destroyAll(SoaState* state) { delete state->spaceSystem; delete state->gameSystem; delete state->systemIoManager; delete state->options; destroyClientState(state->clientState); destroyGameSystem(state); destroySpaceSystem(state); } void SoaEngine::destroyClientState(ClientState& state) { delete state.debugRenderer; delete state.chunkMeshManager; delete state.systemViewer; delete state.blockTextures; } void SoaEngine::destroyGameSystem(SoaState* state) { delete state->gameSystem; } void SoaEngine::destroySpaceSystem(SoaState* state) { delete state->spaceSystem; } ================================================ FILE: SoA/SoaEngine.h ================================================ /// /// SoAEngine.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 10 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Handles initialization and destruction of SoAState /// #pragma once #ifndef SoAEngine_h__ #define SoAEngine_h__ #include #include #include "OptionsController.h" #include "SpaceSystemLoader.h" class GameSystem; class BlockPack; class SpaceSystem; struct SoaState; struct ClientState; struct PlanetGenData; DECL_VCORE(class RPCManager) #pragma once class SoaEngine { public: /// Initializes the default SoaOptions static void initOptions(SoaOptions& options); /// Initializes SoaState resources static void initState(SoaState* state); static void initClientState(SoaState* soaState, ClientState& state); /// Loads and initializes the SpaceSystem static bool loadSpaceSystem(SoaState* state, const nString& filePath); /// Loads and initializes the GameSystem static bool loadGameSystem(SoaState* state); /// Sets block IDs for planet data static void initVoxelGen(PlanetGenData* genData, const BlockPack& blocks); static void reloadSpaceBody(SoaState* state, vecs::EntityID eid, vcore::RPCManager* glRPC); /// Destroys the SoaState completely static void destroyAll(SoaState* state); static void destroyClientState(ClientState& state); static void destroyGameSystem(SoaState* state); static void destroySpaceSystem(SoaState* state); static OptionsController optionsController; }; #endif // SoAEngine_h__ ================================================ FILE: SoA/SoaFileSystem.cpp ================================================ #include "stdafx.h" #include "SoaFileSystem.h" void SoaFileSystem::init() { m_roots["Music"] = vio::IOManager("Data/Music"); } const vio::IOManager& SoaFileSystem::get(const nString& name) const { return m_roots.at(name); } ================================================ FILE: SoA/SoaFileSystem.h ================================================ /// /// SoaFilesystem.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 11 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Provides utilities for operating within the SoA filesystem /// #pragma once #ifndef SoaFilesystem_h__ #define SoaFilesystem_h__ #include class SoaFileSystem { public: void init(); const vio::IOManager& get(const nString& name) const; private: std::unordered_map m_roots; ///< IOManagers mapped by friendly file system names }; #endif // SoaFilesystem_h__ ================================================ FILE: SoA/SoaOptions.cpp ================================================ #include "stdafx.h" #include "SoaOptions.h" #include #include #include "GameManager.h" #include "InputMapper.h" SoaOptions soaOptions; SoaOptions::SoaOptions() { m_options.reserve(OPT_NUM_OPTIONS); } SoaOptions::~SoaOptions() { // Empty } void SoaOptions::addOption(int id, const nString& name, OptionValue defaultValue, SoaOptionFlags flags) { if (id >= (int)m_options.size()) { m_options.resize(id + 1); m_options[id].id = id; m_options[id].name = name; m_options[id].defaultValue = defaultValue; m_options[id].value = defaultValue; m_options[id].flags = flags; m_optionsLookup[name] = id; } } void SoaOptions::addOption(const nString& name, OptionValue defaultValue, SoaOptionFlags flags) { m_options.emplace_back(); SoaOption& option = m_options.back(); option.id = m_options.size() - 1; option.name = name; option.defaultValue = defaultValue; option.value = defaultValue; option.flags = flags; m_optionsLookup[name] = option.id; } void SoaOptions::addStringOption(const nString& name, const nString& defaultValue) { SoaStringOption option; option.name = name; option.defaultValue = defaultValue; option.value = defaultValue; m_stringOptionsLookup[name] = option; } int SoaOptions::findID(const nString& name) { auto it = m_optionsLookup.find(name); if (it == m_optionsLookup.end()) return -1; return it->second; } SoaOption* SoaOptions::find(const nString& name) { auto it = m_optionsLookup.find(name); if (it == m_optionsLookup.end()) return nullptr; return &m_options[it->second]; } SoaOption& SoaOptions::get(int id) { return m_options.at(id); } SoaOption& SoaOptions::get(const nString& name) { return m_options.at(m_optionsLookup[name]); } SoaStringOption& SoaOptions::getStringOption(const nString& name) { return m_stringOptionsLookup[name]; } void SoaOptions::dispose() { std::vector().swap(m_options); std::map().swap(m_optionsLookup); std::map().swap(m_stringOptionsLookup); } ================================================ FILE: SoA/SoaOptions.h ================================================ #pragma once #include "Errors.h" struct SoaOptionFlags { bool needsWindowReload : 1; bool needsFBOReload : 1; bool needsShaderReload : 1; bool needsTextureReload : 1; }; enum class OptionValueType { NONE, F32, I32, BOOL, CHAR }; struct OptionValue { OptionValue() {} OptionValue(f32 f) : f(f), type(OptionValueType::F32) {} OptionValue(i32 i) : i(i), type(OptionValueType::I32) {} OptionValue(bool b) : b(b), type(OptionValueType::BOOL) {} OptionValue(char c) : c(c), type(OptionValueType::CHAR) {} union { f32 f; i32 i; bool b; char c; }; OptionValueType type = OptionValueType::NONE; }; struct SoaOption { int id = -1; nString name = ""; OptionValue defaultValue; OptionValue value; SoaOptionFlags flags; }; struct SoaStringOption { nString name; nString defaultValue; nString value; }; // Integer IDs for faster default options lookups enum DefaultOptions : int { OPT_PLANET_DETAIL = 0, OPT_VOXEL_RENDER_DISTANCE, OPT_HUD_MODE, OPT_TEXTURE_RES, OPT_MOTION_BLUR, OPT_DEPTH_OF_FIELD, OPT_MSAA, OPT_MAX_MSAA, OPT_SPECULAR_EXPONENT, OPT_SPECULAR_INTENSITY, OPT_HDR_EXPOSURE, OPT_GAMMA, OPT_SEC_COLOR_MULT, OPT_FOV, OPT_VSYNC, OPT_MAX_FPS, OPT_VOXEL_LOD_THRESHOLD, OPT_MUSIC_VOLUME, OPT_EFFECT_VOLUME, OPT_MOUSE_SENSITIVITY, OPT_INVERT_MOUSE, OPT_FULLSCREEN, OPT_BORDERLESS, OPT_SCREEN_WIDTH, OPT_SCREEN_HEIGHT, OPT_NUM_OPTIONS // This should be last }; class SoaOptions { public: SoaOptions(); ~SoaOptions(); void addOption(int id, const nString& name, OptionValue defaultValue, SoaOptionFlags flags = {}); void addOption(const nString& name, OptionValue defaultValue, SoaOptionFlags flags = {}); void addStringOption(const nString& name, const nString& defaultValue); int findID(const nString& name); SoaOption* find(const nString& name); SoaOption& get(int id); SoaOption& get(const nString& name); SoaStringOption& getStringOption(const nString& name); const std::vector& getOptions() const { return m_options; } const nString& getFilePath() const { return m_filePath; } void dispose(); private: std::vector m_options; // String lookups are slower than int lookups std::map m_optionsLookup; std::map m_stringOptionsLookup; nString m_filePath = "Data/options.ini"; }; // Global options struct extern SoaOptions soaOptions; ================================================ FILE: SoA/SoaState.cpp ================================================ #include "stdafx.h" #include "SoAState.h" #include #include "DebugRenderer.h" #include "PlanetGenLoader.h" #include "ChunkMeshManager.h" #include "PlanetGenLoader.h" ================================================ FILE: SoA/SonarRenderStage.cpp ================================================ #include "stdafx.h" #include "SonarRenderStage.h" #include #include "Camera.h" #include "Chunk.h" #include "BlockPack.h" #include "BlockTexturePack.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "SoaOptions.h" #include "RenderUtils.h" #include "ShaderLoader.h" // TODO(Ben): Don't hardcode const f32 SONAR_DISTANCE = 200.0f; const f32 SONAR_WIDTH = 30.0f; SonarRenderStage::SonarRenderStage(const GameRenderParams* gameRenderParams) : m_gameRenderParams(gameRenderParams) { // Empty } void SonarRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { glDisable(GL_DEPTH_TEST); ChunkMeshManager* cmm = m_gameRenderParams->chunkMeshmanager; if (!m_program.isCreated()) { m_program = ShaderLoader::createProgramFromFile("Shaders/BlockShading/standardShading.vert", "Shaders/BlockShading/sonarShading.frag"); } m_program.use(); m_program.enableVertexAttribArrays(); // Bind the block textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_ARRAY, m_gameRenderParams->blockTexturePack->getAtlasTexture()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ChunkRenderer::sharedIBO); glUniform1f(m_program.getUniform("sonarDistance"), SONAR_DISTANCE); glUniform1f(m_program.getUniform("waveWidth"), SONAR_WIDTH); glUniform1f(m_program.getUniform("dt"), 1.0f); glUniform1f(m_program.getUniform("fadeDistance"), ChunkRenderer::fadeDist); glDisable(GL_CULL_FACE); glDepthMask(GL_FALSE); const std::vector & chunkMeshes = cmm->getChunkMeshes(); { std::lock_guard l(cmm->lckActiveChunkMeshes); if (chunkMeshes.empty()) return; for (unsigned int i = 0; i < chunkMeshes.size(); i++) { ChunkRenderer::drawOpaqueCustom(chunkMeshes[i], m_program, m_gameRenderParams->chunkCamera->getPosition(), m_gameRenderParams->chunkCamera->getViewProjectionMatrix()); } } glDepthMask(GL_TRUE); glEnable(GL_CULL_FACE); m_program.unuse(); glEnable(GL_DEPTH_TEST); } ================================================ FILE: SoA/SonarRenderStage.h ================================================ /// /// SonarRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file implements the sonar render stage which does a cool /// sonar rendering effect. /// #pragma once #ifndef SonarRenderStage_h__ #define SonarRenderStage_h__ #include "IRenderStage.h" #include class Camera; class GameRenderParams; class MeshManager; class SonarRenderStage : public IRenderStage { public: /// Constructor which injects dependencies /// @param camera: The camera handle /// @param meshManager: Handle to the class that holds meshes SonarRenderStage(const GameRenderParams* gameRenderParams); // Draws the render stage virtual void render(const Camera* camera) override; private: vg::GLProgram m_program; const GameRenderParams* m_gameRenderParams; ///< Handle to shared parameters }; #endif // SonarRenderStage_h__ ================================================ FILE: SoA/SpaceSystem.cpp ================================================ #include "stdafx.h" #include "SpaceSystem.h" #include #include SpaceSystem::SpaceSystem() : vecs::ECS() { // Add in component tables addComponentTable(SPACE_SYSTEM_CT_NAMEPOSITIION_NAME, &namePosition); addComponentTable(SPACE_SYSTEM_CT_AXISROTATION_NAME, &axisRotation); addComponentTable(SPACE_SYSTEM_CT_ORBIT_NAME, &orbit); addComponentTable(SPACE_SYSTEM_CT_SPHERICALTERRAIN_NAME, &sphericalTerrain); addComponentTable(SPACE_SYSTEM_CT_GASGIANT_NAME, &gasGiant); addComponentTable(SPACE_SYSTEM_CT_STAR_NAME, &star); addComponentTable(SPACE_SYSTEM_CT_FARTERRAIN_NAME, &farTerrain); addComponentTable(SPACE_SYSTEM_CT_SPHERICALGRAVITY_NAME, &sphericalGravity); addComponentTable(SPACE_SYSTEM_CT_SPHERICALVOXEL_NAME, &sphericalVoxel); addComponentTable(SPACE_SYSTEM_CT_SPACELIGHT_NAME, &spaceLight); addComponentTable(SPACE_SYSTEM_CT_ATMOSPHERE_NAME, &atmosphere); addComponentTable(SPACE_SYSTEM_CT_PLANETRINGS_NAME, &planetRings); addComponentTable(SPACE_SYSTEM_CT_CLOUDS_NAME, &clouds); } SpaceSystem::~SpaceSystem() { for (auto& it : sphericalVoxel) { sphericalVoxel.disposeComponent(sphericalVoxel.getComponentID(it.first), it.first); } for (auto& it : orbit) { orbit.disposeComponent(orbit.getComponentID(it.first), it.first); } for (auto& it : sphericalVoxel) { sphericalVoxel.disposeComponent(sphericalVoxel.getComponentID(it.first), it.first); } for (auto& it : sphericalTerrain) { sphericalTerrain.disposeComponent(sphericalTerrain.getComponentID(it.first), it.first); } } vecs::ComponentID SpaceSystem::getComponent(nString name, vecs::EntityID eID) { auto& table = *getComponentTable(name); return table.getComponentID(eID); } ================================================ FILE: SoA/SpaceSystem.h ================================================ /// /// SpaceSystem.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Implementation of a Star System with ECS /// #pragma once #ifndef SpaceSystem_h__ #define SpaceSystem_h__ #include "SpaceSystemComponents.h" #include "SpaceSystemComponentTables.h" #include #include #include #include #include #define SPACE_SYSTEM_CT_NAMEPOSITIION_NAME "NamePosition" #define SPACE_SYSTEM_CT_AXISROTATION_NAME "AxisRotation" #define SPACE_SYSTEM_CT_ORBIT_NAME "Orbit" #define SPACE_SYSTEM_CT_SPHERICALTERRAIN_NAME "SphericalTerrain" #define SPACE_SYSTEM_CT_FARTERRAIN_NAME "FarTerrain" #define SPACE_SYSTEM_CT_SPHERICALGRAVITY_NAME "SphericalGravity" #define SPACE_SYSTEM_CT_GASGIANT_NAME "GasGiant" #define SPACE_SYSTEM_CT_STAR_NAME "Star" #define SPACE_SYSTEM_CT_SPHERICALVOXEL_NAME "SphericalVoxel" #define SPACE_SYSTEM_CT_SPACELIGHT_NAME "SpaceLight" #define SPACE_SYSTEM_CT_ATMOSPHERE_NAME "Atmosphere" #define SPACE_SYSTEM_CT_PLANETRINGS_NAME "PlanetRings" #define SPACE_SYSTEM_CT_CLOUDS_NAME "Clouds" class App; class Binary; class Camera; class GameSystem; class PlanetGenLoader; struct SoaState; class SpriteBatch; class SpriteFont; struct GasGiantProperties; struct PlanetProperties; struct StarProperties; struct SystemBody; struct SystemOrbitProperties; DECL_VG(class TextureRecycler) class SpaceSystem : public vecs::ECS { friend class SpaceSystemRenderStage; friend class MainMenuSystemViewer; public: SpaceSystem(); ~SpaceSystem(); NamePositionComponentTable namePosition; AxisRotationComponentTable axisRotation; OrbitComponentTable orbit; SphericalGravityComponentTable sphericalGravity; SphericalTerrainComponentTable sphericalTerrain; GasGiantComponentTable gasGiant; StarComponentTable star; FarTerrainComponentTable farTerrain; SpaceLightComponentTable spaceLight; AtmosphereComponentTable atmosphere; PlanetRingsComponentTable planetRings; CloudsComponentTable clouds; SphericalVoxelComponentTable sphericalVoxel; f32 age = 0.0f; ///< age of the system nString systemDescription = "No description"; ///< textual description of the system // vVv TODO(Cristian): Holy fuck, get rid of these from here vVv std::map > pathColorMap; ///< Map of body type to path colors vecs::ComponentID getComponent(nString name, vecs::EntityID eID); private: VORB_NON_COPYABLE(SpaceSystem); }; #endif // SpaceSystem_h__ ================================================ FILE: SoA/SpaceSystemAssemblages.cpp ================================================ #include "stdafx.h" #include "SpaceSystemAssemblages.h" #include "ChunkGrid.h" #include "ChunkIOManager.h" #include "ChunkAllocator.h" #include "FarTerrainPatch.h" #include "OrbitComponentUpdater.h" #include "SoAState.h" #include "SpaceSystem.h" #include "SphericalTerrainComponentUpdater.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatchMeshManager.h" #include "SpaceSystemAssemblages.h" #include "SpaceSystemLoadStructs.h" // TEMPORARY #include "GameManager.h" #include "PlanetGenData.h" #define SEC_PER_HOUR 3600.0 Event SpaceSystemAssemblages::onAddSphericalVoxelComponent; Event SpaceSystemAssemblages::onRemoveSphericalVoxelComponent; vecs::EntityID SpaceSystemAssemblages::createOrbit(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, SystemBody* body, f64 bodyRadius) { body->entity = spaceSystem->addEntity(); const vecs::EntityID& id = body->entity; f64v3 tmpPos(0.0); vecs::ComponentID npCmp = addNamePositionComponent(spaceSystem, id, body->name, tmpPos); SpaceSystemAssemblages::addOrbitComponent(spaceSystem, id, npCmp, sysProps->type, sysProps->e, sysProps->t, sysProps->n, sysProps->p, sysProps->i, sysProps->a); addSphericalGravityComponent(spaceSystem, id, npCmp, bodyRadius, body->mass); return id; } vecs::EntityID SpaceSystemAssemblages::createPlanet(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const PlanetProperties* properties, SystemBody* body, vcore::ThreadPool* threadPool) { body->entity = spaceSystem->addEntity(); const vecs::EntityID& id = body->entity; // const f64v3 up(0.0, 1.0, 0.0); vecs::ComponentID arCmp = addAxisRotationComponent(spaceSystem, id, properties->aTilt, properties->lNorth, 0.0, properties->rotationalPeriod * SEC_PER_HOUR); f64v3 tmpPos(0.0); vecs::ComponentID npCmp = addNamePositionComponent(spaceSystem, id, body->name, tmpPos); addSphericalTerrainComponent(spaceSystem, id, npCmp, arCmp, properties->diameter * 0.5, properties->planetGenData, threadPool); f64 planetRadius = properties->diameter / 2.0; addSphericalGravityComponent(spaceSystem, id, npCmp, planetRadius, properties->mass); const AtmosphereProperties& at = properties->atmosphere; // Check if its active if (at.scaleDepth != -1.0f) { addAtmosphereComponent(spaceSystem, id, npCmp, (f32)planetRadius, (f32)(planetRadius * 1.025), at.kr, at.km, at.g, at.scaleDepth, at.waveLength); } const CloudsProperties& cl = properties->clouds; if (cl.density > 0.0f) { addCloudsComponent(spaceSystem, id, npCmp, (f32)planetRadius, (f32)(planetRadius * 0.0075), cl.color, cl.scale, cl.density); } SpaceSystemAssemblages::addOrbitComponent(spaceSystem, id, npCmp, sysProps->type, sysProps->e, sysProps->t, sysProps->n, sysProps->p, sysProps->i, sysProps->a); return id; } void SpaceSystemAssemblages::destroyPlanet(SpaceSystem* gameSystem VORB_UNUSED, vecs::EntityID planetEntity VORB_UNUSED) { // TODO: implement and remove VORB_UNUSED tags. } vecs::EntityID SpaceSystemAssemblages::createStar(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const StarProperties* properties, SystemBody* body) { body->entity = spaceSystem->addEntity(); const vecs::EntityID& id = body->entity; // const f64v3 up(0.0, 1.0, 0.0); vecs::ComponentID arCmp = addAxisRotationComponent(spaceSystem, id, properties->aTilt, properties->lNorth, 0.0, properties->rotationalPeriod * SEC_PER_HOUR); f64v3 tmpPos(0.0); vecs::ComponentID npCmp = addNamePositionComponent(spaceSystem, id, body->name, tmpPos); f64 radius = properties->diameter / 2.0; addStarComponent(spaceSystem, id, npCmp, arCmp, properties->mass, radius, properties->surfaceTemperature); addSpaceLightComponent(spaceSystem, id, npCmp, color3(255, 255, 255), 1.0f); addSphericalGravityComponent(spaceSystem, id, npCmp, radius, properties->mass); SpaceSystemAssemblages::addOrbitComponent(spaceSystem, id, npCmp, sysProps->type, sysProps->e, sysProps->t, sysProps->n, sysProps->p, sysProps->i, sysProps->a); return id; } void SpaceSystemAssemblages::destroyStar(SpaceSystem* gameSystem VORB_UNUSED, vecs::EntityID planetEntity VORB_UNUSED) { // TODO: implement and remove VORB_UNUSED tags. } /// GasGiant entity vecs::EntityID SpaceSystemAssemblages::createGasGiant(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const GasGiantProperties* properties, SystemBody* body) { body->entity = spaceSystem->addEntity(); const vecs::EntityID& id = body->entity; // const f64v3 up(0.0, 1.0, 0.0); vecs::ComponentID arCmp = addAxisRotationComponent(spaceSystem, id, properties->aTilt, properties->lNorth, 0.0, properties->rotationalPeriod * SEC_PER_HOUR); f64v3 tmpPos(0.0); vecs::ComponentID npCmp = addNamePositionComponent(spaceSystem, id, body->name, tmpPos); f64 radius = properties->diameter / 2.0; addGasGiantComponent(spaceSystem, id, npCmp, arCmp, properties->oblateness, radius, properties->colorMap, properties->rings); addSphericalGravityComponent(spaceSystem, id, npCmp, radius, properties->mass); const AtmosphereProperties& at = properties->atmosphere; // Check if its active if (at.scaleDepth != -1.0f) { addAtmosphereComponent(spaceSystem, id, npCmp, (f32)radius, (f32)(radius * 1.025), at.kr, at.km, at.g, at.scaleDepth, at.waveLength, properties->oblateness); } if (properties->rings.size()) { addPlanetRingsComponent(spaceSystem, id, npCmp, properties->rings); } SpaceSystemAssemblages::addOrbitComponent(spaceSystem, id, npCmp, sysProps->type, sysProps->e, sysProps->t, sysProps->n, sysProps->p, sysProps->i, sysProps->a); return id; } void SpaceSystemAssemblages::destroyGasGiant(SpaceSystem* gameSystem VORB_UNUSED, vecs::EntityID planetEntity VORB_UNUSED) { // TODO: implement and remove VORB_UNUSED tags. } vecs::ComponentID SpaceSystemAssemblages::addAtmosphereComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, f32 planetRadius, f32 radius, f32 kr, f32 km, f32 g, f32 scaleDepth, f32v3 wavelength, f32 oblateness /*= 0.0f*/) { vecs::ComponentID aCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_ATMOSPHERE_NAME, entity); auto& aCmp = spaceSystem->atmosphere.get(aCmpId); aCmp.namePositionComponent = namePositionComponent; aCmp.planetRadius = planetRadius; aCmp.radius = radius; aCmp.oblateness = oblateness; aCmp.kr = kr; aCmp.km = km; aCmp.g = g; aCmp.scaleDepth = scaleDepth; aCmp.invWavelength4 = f32v3(1.0f / powf(wavelength.r, 4.0f), 1.0f / powf(wavelength.g, 4.0f), 1.0f / powf(wavelength.b, 4.0f)); return aCmpId; } void SpaceSystemAssemblages::removeAtmosphereComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_ATMOSPHERE_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addPlanetRingsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, const Array& rings) { vecs::ComponentID prCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_PLANETRINGS_NAME, entity); if (prCmpId == 0) { return 0; } auto& prCmp = spaceSystem->planetRings.get(prCmpId); prCmp.namePositionComponent = namePositionComponent; prCmp.rings.resize(rings.size()); for (size_t i = 0; i < rings.size(); i++) { auto& r1 = prCmp.rings[i]; auto& r2 = rings[i]; r1.innerRadius = r2.innerRadius; r1.outerRadius = r2.outerRadius; r1.texturePath = r2.colorLookup; r1.orientation = glm::angleAxis((f64)r2.lNorth, f64v3(0.0, 1.0, 0.0)) * glm::angleAxis((f64)r2.aTilt, f64v3(1.0, 0.0, 0.0)); } return prCmpId; } void SpaceSystemAssemblages::removePlanetRingsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_PLANETRINGS_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addCloudsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, f32 planetRadius, f32 height, f32v3 color, f32v3 scale, float density) { vecs::ComponentID cCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_CLOUDS_NAME, entity); auto& cCmp = spaceSystem->clouds.get(cCmpId); cCmp.namePositionComponent = namePositionComponent; cCmp.planetRadius = planetRadius; cCmp.height = height; cCmp.color = color; cCmp.scale = scale; cCmp.density = density; return cCmpId; } void SpaceSystemAssemblages::removeCloudsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_CLOUDS_NAME, entity); } // TODO: Is this implementation complete? vecs::ComponentID SpaceSystemAssemblages::addSphericalVoxelComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID sphericalTerrainComponent, vecs::ComponentID farTerrainComponent, vecs::ComponentID axisRotationComponent, vecs::ComponentID namePositionComponent, WorldCubeFace worldFace VORB_UNUSED, SoaState* soaState) { vecs::ComponentID svCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_SPHERICALVOXEL_NAME, entity); if (svCmpId == 0) { return 0; } auto& svcmp = spaceSystem->sphericalVoxel.get(svCmpId); auto& ftcmp = spaceSystem->farTerrain.get(farTerrainComponent); // Get component handles svcmp.sphericalTerrainComponent = sphericalTerrainComponent; svcmp.axisRotationComponent = axisRotationComponent; svcmp.namePositionComponent = namePositionComponent; svcmp.farTerrainComponent = farTerrainComponent; svcmp.voxelRadius = ftcmp.sphericalTerrainData->radius * VOXELS_PER_KM; svcmp.generator = ftcmp.cpuGenerator; svcmp.chunkIo = new ChunkIOManager("TESTSAVEDIR"); // TODO(Ben): Fix svcmp.blockPack = &soaState->blocks; svcmp.threadPool = soaState->threadPool; svcmp.chunkIo->beginThread(); svcmp.chunkGrids = new ChunkGrid[6]; for (int i = 0; i < 6; i++) { svcmp.chunkGrids[i].init(static_cast(i), svcmp.threadPool, 1, ftcmp.planetGenData, &soaState->chunkAllocator); svcmp.chunkGrids[i].blockPack = &soaState->blocks; } svcmp.planetGenData = ftcmp.planetGenData; svcmp.sphericalTerrainData = ftcmp.sphericalTerrainData; svcmp.saveFileIom = &soaState->saveFileIom; onAddSphericalVoxelComponent(svcmp, entity); return svCmpId; } void SpaceSystemAssemblages::removeSphericalVoxelComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { onRemoveSphericalVoxelComponent(spaceSystem->sphericalVoxel.getFromEntity(entity), entity); spaceSystem->deleteComponent(SPACE_SYSTEM_CT_SPHERICALVOXEL_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addAxisRotationComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, f32 aTilt, f32 lNorth, f64 startAngle, f64 rotationalPeriod) { vecs::ComponentID arCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_AXISROTATION_NAME, entity); auto& arCmp = spaceSystem->axisRotation.get(arCmpId); arCmp.tilt = aTilt; arCmp.axisOrientation = glm::angleAxis((f64)lNorth, f64v3(0.0, 1.0, 0.0)) * glm::angleAxis((f64)aTilt, f64v3(1.0, 0.0, 0.0)); arCmp.currentRotation = startAngle; arCmp.period = rotationalPeriod; return arCmpId; } void SpaceSystemAssemblages::removeAxisRotationComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_AXISROTATION_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addSphericalTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f64 radius, PlanetGenData* planetGenData, vcore::ThreadPool* threadPool) { vecs::ComponentID stCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_SPHERICALTERRAIN_NAME, entity); auto& stCmp = spaceSystem->sphericalTerrain.get(stCmpId); stCmp.namePositionComponent = npComp; stCmp.axisRotationComponent = arComp; stCmp.planetGenData = planetGenData; if (planetGenData) { stCmp.meshManager = new TerrainPatchMeshManager(planetGenData); stCmp.cpuGenerator = new SphericalHeightmapGenerator; stCmp.cpuGenerator->init(planetGenData); } stCmp.radius = radius; stCmp.alpha = 1.0f; f64 patchWidth = (radius * 2.0) / ST_PATCH_ROW; stCmp.sphericalTerrainData = new TerrainPatchData(radius, patchWidth, stCmp.cpuGenerator, stCmp.meshManager, threadPool); return stCmpId; } void SpaceSystemAssemblages::removeSphericalTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { // auto& stcmp = spaceSystem->sphericalTerrain.getFromEntity(entity); spaceSystem->deleteComponent(SPACE_SYSTEM_CT_SPHERICALTERRAIN_NAME, entity); } /// Star Component vecs::ComponentID SpaceSystemAssemblages::addStarComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f64 mass, f64 radius, f64 temperature) { vecs::ComponentID sCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_STAR_NAME, entity); auto& sCmp = spaceSystem->star.get(sCmpId); sCmp.namePositionComponent = npComp; sCmp.axisRotationComponent = arComp; sCmp.radius = radius; sCmp.temperature = temperature; sCmp.mass = mass; sCmp.occlusionQuery[0] = 0; sCmp.occlusionQuery[1] = 0; return sCmpId; } void SpaceSystemAssemblages::removeStarComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_STAR_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addGasGiantComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f32 oblateness, f64 radius, const nString& colorMapPath, const Array& rings) { vecs::ComponentID ggCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_GASGIANT_NAME, entity); auto& ggCmp = spaceSystem->gasGiant.get(ggCmpId); ggCmp.namePositionComponent = npComp; ggCmp.axisRotationComponent = arComp; ggCmp.oblateness = oblateness; ggCmp.radius = radius; ggCmp.colorMapPath = colorMapPath; ggCmp.rings = rings; return ggCmpId; } void SpaceSystemAssemblages::removeGasGiantComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_GASGIANT_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addFarTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, SphericalTerrainComponent& parentCmp, WorldCubeFace face) { vecs::ComponentID ftCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_FARTERRAIN_NAME, entity); auto& ftCmp = spaceSystem->farTerrain.get(ftCmpId); ftCmp.planetGenData = parentCmp.planetGenData; ftCmp.meshManager = parentCmp.meshManager; ftCmp.cpuGenerator = parentCmp.cpuGenerator; ftCmp.sphericalTerrainData = parentCmp.sphericalTerrainData; ftCmp.face = face; ftCmp.alpha = TERRAIN_INC_START_ALPHA; return ftCmpId; } void SpaceSystemAssemblages::removeFarTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { auto& ftcmp = spaceSystem->farTerrain.getFromEntity(entity); if (ftcmp.patches) delete[] ftcmp.patches; ftcmp.patches = nullptr; spaceSystem->deleteComponent(SPACE_SYSTEM_CT_FARTERRAIN_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addSphericalGravityComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, f64 radius, f64 mass) { vecs::ComponentID sgCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_SPHERICALGRAVITY_NAME, entity); auto& sgCmp = spaceSystem->sphericalGravity.get(sgCmpId); sgCmp.namePositionComponent = npComp; sgCmp.radius = radius; sgCmp.mass = mass; return sgCmpId; } void SpaceSystemAssemblages::removeSphericalGravityComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_SPHERICALGRAVITY_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addNamePositionComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, const nString& name, const f64v3& position) { vecs::ComponentID npCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_NAMEPOSITIION_NAME, entity); auto& npCmp = spaceSystem->namePosition.get(npCmpId); npCmp.name = name; npCmp.position = position; return npCmpId; } void SpaceSystemAssemblages::removeNamePositionComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_NAMEPOSITIION_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addOrbitComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, SpaceObjectType oType, f64 eccentricity, f64 orbitalPeriod, f64 ascendingLong, f64 periapsisLong, f64 inclination, f64 trueAnomaly) { vecs::ComponentID oCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_ORBIT_NAME, entity); auto& oCmp = spaceSystem->orbit.get(oCmpId); oCmp.e = eccentricity; oCmp.t = orbitalPeriod; oCmp.npID = npComp; oCmp.o = ascendingLong * DEG_TO_RAD; oCmp.p = periapsisLong * DEG_TO_RAD; oCmp.i = inclination * DEG_TO_RAD; oCmp.startMeanAnomaly = trueAnomaly * DEG_TO_RAD; oCmp.type = oType; // Get the path color std::pair pathColor(f32v4(0.0f), f32v4(0.0f)); switch (oType) { case SpaceObjectType::STAR: pathColor = spaceSystem->pathColorMap["Star"]; break; case SpaceObjectType::PLANET: pathColor = spaceSystem->pathColorMap["Planet"]; break; case SpaceObjectType::DWARF_PLANET: pathColor = spaceSystem->pathColorMap["DwarfPlanet"]; break; case SpaceObjectType::MOON: pathColor = spaceSystem->pathColorMap["Moon"]; break; case SpaceObjectType::DWARF_MOON: pathColor = spaceSystem->pathColorMap["DwarfMoon"]; break; case SpaceObjectType::ASTEROID: pathColor = spaceSystem->pathColorMap["Asteroid"]; break; case SpaceObjectType::COMET: pathColor = spaceSystem->pathColorMap["Comet"]; break; default: break; } oCmp.pathColor[0] = pathColor.first; oCmp.pathColor[1] = pathColor.second; return oCmpId; } void SpaceSystemAssemblages::removeOrbitComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_ORBIT_NAME, entity); } vecs::ComponentID SpaceSystemAssemblages::addSpaceLightComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npCmp, color3 color, f32 intensity) { vecs::ComponentID slCmpId = spaceSystem->addComponent(SPACE_SYSTEM_CT_SPACELIGHT_NAME, entity); auto& slCmp = spaceSystem->spaceLight.get(slCmpId); slCmp.color = color; slCmp.intensity = intensity; slCmp.npID = npCmp; return slCmpId; } void SpaceSystemAssemblages::removeSpaceLightComponent(SpaceSystem* spaceSystem, vecs::EntityID entity) { spaceSystem->deleteComponent(SPACE_SYSTEM_CT_SPACELIGHT_NAME, entity); } ================================================ FILE: SoA/SpaceSystemAssemblages.h ================================================ /// /// SpaceSystemAssemblages.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 13 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Component and entity assemblages for SpaceSystem /// #pragma once #ifndef SpaceSystemAssemblages_h__ #define SpaceSystemAssemblages_h__ class SpaceSystem; #include "VoxelCoordinateSpaces.h" #include "SpaceSystemLoadStructs.h" #include "VoxPool.h" #include #include #include struct PlanetGenData; struct GasGiantProperties; struct PlanetProperties; struct SoaState; struct SphericalTerrainComponent; struct SphericalVoxelComponent; struct StarProperties; struct SystemBody; struct SystemOrbitProperties; DECL_VG( class GLProgram; class TextureRecycler; ) DECL_VVOX(class VoxelMapData); enum class SpaceObjectType; namespace SpaceSystemAssemblages { /************************************************************************/ /* Entity Factories */ /************************************************************************/ // Plain orbit entity extern vecs::EntityID createOrbit(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, SystemBody* body, f64 bodyRadius); /// Planet entity extern vecs::EntityID createPlanet(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const PlanetProperties* properties, SystemBody* body, vcore::ThreadPool* threadPool); extern void destroyPlanet(SpaceSystem* gameSystem, vecs::EntityID planetEntity); /// Star entity extern vecs::EntityID createStar(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const StarProperties* properties, SystemBody* body); extern void destroyStar(SpaceSystem* gameSystem, vecs::EntityID planetEntity); /// GasGiant entity extern vecs::EntityID createGasGiant(SpaceSystem* spaceSystem, const SystemOrbitProperties* sysProps, const GasGiantProperties* properties, SystemBody* body); extern void destroyGasGiant(SpaceSystem* gameSystem, vecs::EntityID planetEntity); /************************************************************************/ /* Component Assemblages */ /************************************************************************/ /// Atmosphere component extern vecs::ComponentID addAtmosphereComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, f32 planetRadius, f32 radius, f32 kr, f32 km, f32 g, f32 scaleDepth, f32v3 wavelength, f32 oblateness = 0.0f); extern void removeAtmosphereComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// PlanetRings component extern vecs::ComponentID addPlanetRingsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, const Array& rings); extern void removePlanetRingsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Clouds component extern vecs::ComponentID addCloudsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID namePositionComponent, f32 planetRadius, f32 height, f32v3 color, f32v3 scale, float density); extern void removeCloudsComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Spherical voxel component extern vecs::ComponentID addSphericalVoxelComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID sphericalTerrainComponent, vecs::ComponentID farTerrainComponent, vecs::ComponentID axisRotationComponent, vecs::ComponentID namePositionComponent, WorldCubeFace worldFace, SoaState* soaState); extern void removeSphericalVoxelComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); extern Event onAddSphericalVoxelComponent; extern Event onRemoveSphericalVoxelComponent; /// Axis rotation component extern vecs::ComponentID addAxisRotationComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, f32 aTilt, f32 lNorth, f64 startAngle, f64 rotationalPeriod); extern void removeAxisRotationComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Spherical terrain component extern vecs::ComponentID addSphericalTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f64 radius, PlanetGenData* planetGenData, vcore::ThreadPool* threadPool); extern void removeSphericalTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Star Component extern vecs::ComponentID addStarComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f64 mass, f64 radius, f64 temperature); extern void removeStarComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Gas giant component extern vecs::ComponentID addGasGiantComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, vecs::ComponentID arComp, f32 oblateness, f64 radius, const nString& colorMapPath, const Array& rings); extern void removeGasGiantComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Far terrain component extern vecs::ComponentID addFarTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, SphericalTerrainComponent& parentCmp, WorldCubeFace face); extern void removeFarTerrainComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Spherical Gravity component extern vecs::ComponentID addSphericalGravityComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, f64 radius, f64 mass); extern void removeSphericalGravityComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Name Position component extern vecs::ComponentID addNamePositionComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, const nString& name, const f64v3& position); extern void removeNamePositionComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Orbit component extern vecs::ComponentID addOrbitComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npComp, SpaceObjectType oType, f64 eccentricity, f64 orbitalPeriod, f64 ascendingLong, f64 periapsisLong, f64 inclination, f64 trueAnomaly); extern void removeOrbitComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); /// Space Light Component extern vecs::ComponentID addSpaceLightComponent(SpaceSystem* spaceSystem, vecs::EntityID entity, vecs::ComponentID npCmp, color3 color, f32 intensity); extern void removeSpaceLightComponent(SpaceSystem* spaceSystem, vecs::EntityID entity); } #endif // SpaceSystemAssemblages_h__ ================================================ FILE: SoA/SpaceSystemComponentBuilders.cpp ================================================ #include "stdafx.h" #include "SpaceSystemComponentBuilders.h" ================================================ FILE: SoA/SpaceSystemComponentBuilders.h ================================================ #pragma once #ifndef SpaceSystemComponentBuilders_h__ #define SpaceSystemComponentBuilders_h__ #endif //SpaceSystemComponentBuilders_h__ ================================================ FILE: SoA/SpaceSystemComponentTables.cpp ================================================ #include "stdafx.h" #include "SpaceSystemComponentTables.h" #include #include #include "ChunkAllocator.h" #include "ChunkIOManager.h" #include "FarTerrainPatch.h" #include "ChunkGrid.h" #include "PlanetGenData.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatch.h" #include "TerrainPatchMeshManager.h" void SphericalVoxelComponentTable::disposeComponent(vecs::ComponentID cID, vecs::EntityID eID VORB_MAYBE_UNUSED) { SphericalVoxelComponent& cmp = _components[cID].second; // Let the threadpool finish while (cmp.threadPool->getTasksSizeApprox() > 0); delete cmp.chunkIo; delete[] cmp.chunkGrids; cmp = _components[0].second; } void SphericalTerrainComponentTable::disposeComponent(vecs::ComponentID cID, vecs::EntityID eID VORB_MAYBE_UNUSED) { SphericalTerrainComponent& cmp = _components[cID].second; if (cmp.patches) { delete[] cmp.patches; cmp.patches = nullptr; } if (cmp.planetGenData) { delete cmp.meshManager; delete cmp.cpuGenerator; } // TODO(Ben): Memory leak delete cmp.sphericalTerrainData; } void FarTerrainComponentTable::disposeComponent(vecs::ComponentID cID, vecs::EntityID eID VORB_MAYBE_UNUSED) { FarTerrainComponent& cmp = _components[cID].second; if (cmp.patches) { delete[] cmp.patches; cmp.patches = nullptr; } } void OrbitComponentTable::disposeComponent(vecs::ComponentID cID, vecs::EntityID eID VORB_MAYBE_UNUSED) { OrbitComponent& cmp = _components[cID].second; if (cmp.vbo) { vg::GpuMemory::freeBuffer(cmp.vbo); } } ================================================ FILE: SoA/SpaceSystemComponentTables.h ================================================ /// /// SpaceSystemComponentTables.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 15 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// File for component tables that need custom deletion /// #pragma once #ifndef SpaceSystemComponentTables_h__ #define SpaceSystemComponentTables_h__ #include #include "SpaceSystemComponents.h" class SphericalVoxelComponentTable : public vecs::ComponentTable { public: virtual void disposeComponent(vecs::ComponentID cID, vecs::EntityID eID) override; }; class SphericalTerrainComponentTable : public vecs::ComponentTable < SphericalTerrainComponent > { public: virtual void disposeComponent(vecs::ComponentID cID, vecs::EntityID eID) override; }; class FarTerrainComponentTable : public vecs::ComponentTable < FarTerrainComponent > { public: virtual void disposeComponent(vecs::ComponentID cID, vecs::EntityID eID) override; }; class OrbitComponentTable : public vecs::ComponentTable < OrbitComponent > { public: virtual void disposeComponent(vecs::ComponentID cID, vecs::EntityID eID) override; }; #endif // SpaceSystemComponentTables_h__ ================================================ FILE: SoA/SpaceSystemComponents.h ================================================ /// /// SpaceSystemComponents.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 20 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Component definitions for SpaceSystem /// #pragma once #ifndef SpaceSystemComponents_h__ #define SpaceSystemComponents_h__ #include #include #include #include #include #include #include #include "Constants.h" #include "SpaceSystemLoadStructs.h" #include "VoxPool.h" #include "VoxelCoordinateSpaces.h" #include "VoxelLightEngine.h" #include "ChunkGrid.h" class BlockPack; class ChunkIOManager; class ChunkManager; class FarTerrainPatch; class PagedChunkAllocator; class ParticleEngine; class PhysicsEngine; class SphericalHeightmapGenerator; class SphericalTerrainGpuGenerator; class TerrainPatch; class TerrainPatchMeshManager; class TerrainRpcDispatcher; struct PlanetGenData; struct TerrainPatchData; DECL_VVOX(class VoxelPlanetMapper); DECL_VIO(class IOManager); /// For far and spherical terrain patch blending on transitions const f32 TERRAIN_FADE_LENGTH = 2.0f; const f32 TERRAIN_ALPHA_BEFORE_FADE = 2.0f; const f32 TERRAIN_DEC_START_ALPHA = TERRAIN_ALPHA_BEFORE_FADE + TERRAIN_FADE_LENGTH; const f32 TERRAIN_INC_START_ALPHA = -TERRAIN_ALPHA_BEFORE_FADE; const f32 TERRAIN_ALPHA_STEP = 0.01f; const f32 START_FACE_TRANS = 1.0f; struct AtmosphereComponent { vecs::ComponentID namePositionComponent = 0; f32 planetRadius; f32 radius; f32 oblateness = 0.0f; f32 kr = 0.0025f; f32 km = 0.0020f; f32 esun = 30.0f; // TODO(Ben): This should be dynamic f32 g = -0.99f; f32 scaleDepth = 0.25f; f32v3 invWavelength4 = f32v3(1.0f / powf(0.65f, 4.0f), 1.0f / powf(0.57f, 4.0f), 1.0f / powf(0.475f, 4.0f)); }; typedef vecs::ComponentTable AtmosphereComponentTable; KEG_TYPE_DECL(AtmosphereComponent); struct CloudsComponent { vecs::ComponentID namePositionComponent = 0; f32 planetRadius; f32 height; f32v3 color; f32v3 scale; float density; }; typedef vecs::ComponentTable CloudsComponentTable; KEG_TYPE_DECL(CloudsComponent); struct AxisRotationComponent { f64q axisOrientation; ///< Axis of rotation f64q currentOrientation; ///< Current orientation with axis and rotation f64q invCurrentOrientation; ///< Inverse of currentOrientation f64 period = 0.0; ///< Period of rotation in seconds f64 currentRotation = 0.0; ///< Current rotation about axis in radians f32 tilt = 0.0f; }; typedef vecs::ComponentTable AxisRotationComponentTable; KEG_TYPE_DECL(AxisRotationComponent); struct NamePositionComponent { f64v3 position = f64v3(0.0); ///< Position in space, in KM nString name; ///< Name of the entity }; typedef vecs::ComponentTable NamePositionComponentTable; KEG_TYPE_DECL(NamePositionComponent); struct SpaceLightComponent { vecs::ComponentID npID; ///< Component ID of parent NamePosition component color3 color; ///< Color of the light f32 intensity; ///< Intensity of the light }; typedef vecs::ComponentTable SpaceLightComponentTable; KEG_TYPE_DECL(SpaceLightComponent); struct OrbitComponent { f64 a = 0.0; ///< Semi-major of the ellipse in KM f64 b = 0.0; ///< Semi-minor of the ellipse in KM f64 t = 0.0; ///< Period of a full orbit in sec f64 parentMass = 0.0; ///< Mass of the parent in KG f64 startMeanAnomaly = 0.0; ///< Start mean anomaly in rad f64 e = 0.0; ///< Shape of orbit, 0-1 f64 o = 0.0; ///< Longitude of the ascending node in rad f64 p = 0.0; ///< Longitude of the periapsis in rad f64 i = 0.0; ///< Inclination in rad f64v3 velocity = f64v3(0.0); ///< Current velocity relative to space in KM/s f64v3 relativeVelocity = f64v3(0.0); ///< Current velocity relative to parent in KM/s f32v4 pathColor[2]; ///< Color of the path vecs::ComponentID npID = 0; ///< Component ID of NamePosition component vecs::ComponentID parentOrbId = 0; ///< Component ID of parent OrbitComponent VGBuffer vbo = 0; ///< vbo for the ellipse mesh VGBuffer vao = 0; ///< vao for the ellipse mesh ui32 numVerts = 0; ///< Number of vertices in the ellipse struct Vertex { f32v3 position; f32 angle; }; std::vector verts; ///< Vertices for the ellipse f32 currentMeanAnomaly; SpaceObjectType type; ///< Type of object bool isCalculated = false; ///< True when orbit has been calculated }; KEG_TYPE_DECL(OrbitComponent); struct PlanetRing { f32 innerRadius; f32 outerRadius; f64q orientation; vio::Path texturePath; }; struct PlanetRingsComponent { vecs::ComponentID namePositionComponent; std::vector rings; }; typedef vecs::ComponentTable PlanetRingsComponentTable; KEG_TYPE_DECL(PlanetRingsComponent); struct SphericalGravityComponent { vecs::ComponentID namePositionComponent; ///< Component ID of parent NamePosition component f64 radius = 0.0; ///< Radius in KM f64 mass = 0.0; ///< Mass in KG }; typedef vecs::ComponentTable SphericalGravityComponentTable; KEG_TYPE_DECL(SphericalGravityComponent); struct SphericalVoxelComponent { ChunkGrid* chunkGrids = nullptr; // should be size 6, one for each face ChunkIOManager* chunkIo = nullptr; SphericalHeightmapGenerator* generator = nullptr; PlanetGenData* planetGenData = nullptr; const TerrainPatchData* sphericalTerrainData = nullptr; const vio::IOManager* saveFileIom = nullptr; const BlockPack* blockPack = nullptr; vecs::ComponentID sphericalTerrainComponent = 0; vecs::ComponentID farTerrainComponent = 0; vecs::ComponentID namePositionComponent = 0; vecs::ComponentID axisRotationComponent = 0; /// The threadpool for generating chunks and meshes vcore::ThreadPool* threadPool = nullptr; int numCaTasks = 0; /// TODO(Ben): Explore alternative f64 voxelRadius = 0; ///< Radius of the planet in voxels int refCount = 1; ui32 updateCount = 0; }; KEG_TYPE_DECL(SphericalVoxelComponent); struct SphericalTerrainComponent { vecs::ComponentID namePositionComponent = 0; vecs::ComponentID axisRotationComponent = 0; vecs::ComponentID sphericalVoxelComponent = 0; vecs::ComponentID farTerrainComponent = 0; TerrainPatch* patches = nullptr; ///< Buffer for top level patches TerrainPatchData* sphericalTerrainData = nullptr; TerrainPatchMeshManager* meshManager = nullptr; SphericalHeightmapGenerator* cpuGenerator = nullptr; PlanetGenData* planetGenData = nullptr; VoxelPosition3D startVoxelPosition; bool needsVoxelComponent = false; WorldCubeFace transitionFace = FACE_NONE; f32 alpha = 0.0f; ///< Alpha blending coefficient f32 faceTransTime = START_FACE_TRANS; ///< For animation on fade f64 distance = FLT_MAX; f64 radius = 0.0; bool isFaceTransitioning = false; volatile bool needsFaceTransitionAnimation = false; }; KEG_TYPE_DECL(SphericalTerrainComponent); struct GasGiantComponent { vecs::ComponentID namePositionComponent = 0; vecs::ComponentID axisRotationComponent = 0; f64 radius = 0.0; f32 oblateness = 1.0f; nString colorMapPath = ""; Array rings; }; typedef vecs::ComponentTable GasGiantComponentTable; KEG_TYPE_DECL(GasGiantComponent); struct StarComponent { vecs::ComponentID namePositionComponent = 0; vecs::ComponentID axisRotationComponent = 0; f64 mass = 0.0; ///< In KG f64 radius = 0.0; ///< In KM f64 temperature = 0.0; ///< In kelvin VGQuery occlusionQuery[2]; ///< TODO(Ben): Delete this f32 visibility = 1.0; }; typedef vecs::ComponentTable StarComponentTable; KEG_TYPE_DECL(StarComponent); struct FarTerrainComponent { TerrainRpcDispatcher* rpcDispatcher = nullptr; FarTerrainPatch* patches = nullptr; ///< Buffer for top level patches TerrainPatchData* sphericalTerrainData = nullptr; TerrainPatchMeshManager* meshManager = nullptr; SphericalHeightmapGenerator* cpuGenerator = nullptr; vcore::ThreadPool* threadPool = nullptr; WorldCubeFace face = FACE_NONE; PlanetGenData* planetGenData = nullptr; i32v2 center = i32v2(0); ///< Center, in units of patch width, where camera is i32v2 origin = i32v2(0); ///< Specifies which patch is the origin (back left corner) on the grid WorldCubeFace transitionFace = FACE_NONE; f32 alpha = 1.0f; ///< Alpha blending coefficient bool shouldFade = false; ///< When this is true we fade out }; KEG_TYPE_DECL(FarTerrainComponent); #endif // SpaceSystemComponents_h__ ================================================ FILE: SoA/SpaceSystemLoadStructs.cpp ================================================ #include "stdafx.h" #include "SpaceSystemLoadStructs.h" KEG_ENUM_DEF(SpaceBodyType, SpaceBodyType, kt) { kt.addValue("planet", SpaceBodyType::PLANET); kt.addValue("star", SpaceBodyType::STAR); kt.addValue("gasGiant", SpaceBodyType::GAS_GIANT); } KEG_ENUM_DEF(SpaceObjectType, SpaceObjectType, kt) { kt.addValue("Barycenter", SpaceObjectType::BARYCENTER); kt.addValue("Star", SpaceObjectType::STAR); kt.addValue("Planet", SpaceObjectType::PLANET); kt.addValue("DwarfPlanet", SpaceObjectType::DWARF_PLANET); kt.addValue("Moon", SpaceObjectType::MOON); kt.addValue("DwarfMoon", SpaceObjectType::DWARF_MOON); kt.addValue("Asteroid", SpaceObjectType::ASTEROID); kt.addValue("Comet", SpaceObjectType::COMET); } std::string spaceObjectTypeName(const SpaceObjectType &type) { static std::unordered_map SpaceObjectTypeNames= { {SpaceObjectType::NONE, "none"}, {SpaceObjectType::BARYCENTER, "barycenter"}, {SpaceObjectType::STAR, "star"}, {SpaceObjectType::PLANET, "planet"}, {SpaceObjectType::DWARF_PLANET, "dwarfplanet"}, {SpaceObjectType::MOON, "moon"}, {SpaceObjectType::DWARF_MOON, "dwarfmoon"}, {SpaceObjectType::ASTEROID, "asteroid"}, {SpaceObjectType::COMET, "comet"} }; return SpaceObjectTypeNames[type]; } KEG_ENUM_DEF(TrojanType, TrojanType, kt) { kt.addValue("L4", TrojanType::L4); kt.addValue("L5", TrojanType::L5); } KEG_TYPE_DEF_SAME_NAME(SystemOrbitProperties, kt) { kt.addValue("type", keg::Value::custom(offsetof(SystemOrbitProperties, type), "SpaceObjectType", true)); kt.addValue("trojan", keg::Value::custom(offsetof(SystemOrbitProperties, trojan), "TrojanType", true)); kt.addValue("comps", keg::Value::array(offsetof(SystemOrbitProperties, comps), keg::BasicType::C_STRING)); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, par, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, path, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, ref, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, e, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, t, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, a, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, n, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, p, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, i, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, RA, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, dec, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, dist, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, td, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, SystemOrbitProperties, tf, F64); } KEG_TYPE_DEF_SAME_NAME(AtmosphereProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, AtmosphereProperties, kr, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, AtmosphereProperties, km, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, AtmosphereProperties, g, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, AtmosphereProperties, scaleDepth, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, AtmosphereProperties, waveLength, F32_V3); } KEG_TYPE_DEF_SAME_NAME(PlanetRingProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetRingProperties, innerRadius, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetRingProperties, outerRadius, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetRingProperties, aTilt, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetRingProperties, lNorth, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetRingProperties, colorLookup, STRING); } KEG_TYPE_DEF_SAME_NAME(CloudsProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, CloudsProperties, color, F32_V3); KEG_TYPE_INIT_ADD_MEMBER(kt, CloudsProperties, scale, F32_V3); KEG_TYPE_INIT_ADD_MEMBER(kt, CloudsProperties, density, F32); } KEG_TYPE_DEF_SAME_NAME(PlanetProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, diameter, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, density, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, mass, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, aTilt, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, lNorth, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, rotationalPeriod, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, displayName, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, PlanetProperties, generation, STRING); kt.addValue("atmosphere", keg::Value::custom(offsetof(PlanetProperties, atmosphere), "AtmosphereKegProperties", false)); kt.addValue("clouds", keg::Value::custom(offsetof(PlanetProperties, clouds), "CloudsKegProperties", false)); } KEG_TYPE_DEF_SAME_NAME(StarProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, surfaceTemperature, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, diameter, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, density, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, mass, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, aTilt, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, lNorth, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, rotationalPeriod, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, StarProperties, displayName, STRING); } KEG_TYPE_DEF_SAME_NAME(GasGiantProperties, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, diameter, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, density, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, mass, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, aTilt, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, lNorth, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, rotationalPeriod, F64); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, oblateness, F32); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, colorMap, STRING); KEG_TYPE_INIT_ADD_MEMBER(kt, GasGiantProperties, displayName, STRING); kt.addValue("atmosphere", keg::Value::custom(offsetof(GasGiantProperties, atmosphere), "AtmosphereKegProperties", false)); kt.addValue("rings", keg::Value::array(offsetof(GasGiantProperties, rings), keg::Value::custom(0, "PlanetRingProperties", false))); } ================================================ FILE: SoA/SpaceSystemLoadStructs.h ================================================ /// /// SpaceSystemLoadStructs.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 20 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Structs for SpaceSystem initialization /// #pragma once #ifndef SpaceSystemLoadStructs_h__ #define SpaceSystemLoadStructs_h__ struct PlanetGenData; #include #include #include #include enum class SpaceBodyType { NONE, PLANET, STAR, GAS_GIANT }; KEG_TYPE_DECL(SpaceBodyType); enum class SpaceObjectType { NONE, BARYCENTER, STAR, PLANET, DWARF_PLANET, MOON, DWARF_MOON, ASTEROID, COMET }; KEG_TYPE_DECL(SpaceObjectType); std::string spaceObjectTypeName(const SpaceObjectType &type); enum class TrojanType { NONE, L4, L5 }; KEG_TYPE_DECL(TrojanType); struct AtmosphereProperties { f32 kr = 0.0025f; f32 km = 0.0020f; f32 g = -0.99f; f32 scaleDepth = 0.25f; f32v3 waveLength = f32v3(0.65, 0.57, 0.475); }; KEG_TYPE_DECL(AtmosphereProperties); struct PlanetRingProperties { f32 innerRadius = 0.0f; f32 outerRadius = 0.0f; f32 aTilt = 0.0f; f32 lNorth = 0.0f; nString colorLookup = ""; }; KEG_TYPE_DECL(PlanetRingProperties); struct CloudsProperties { f32v3 color = f32v3(1.0f, 1.0f, 1.0f); f32v3 scale = f32v3(1.0f, 1.5f, 1.0f); float density = 0.0f; }; KEG_TYPE_DECL(CloudsProperties); struct SystemOrbitProperties { SpaceObjectType type = SpaceObjectType::NONE; TrojanType trojan = TrojanType::NONE; Array comps; nString par = ""; ///< Parent name nString path = ""; ///< Path to properties nString ref = ""; ///< Orbital period reference body f64 e = 0.0; ///< Shape of orbit, 0-1 f64 t = 0.0; ///< Period of a full orbit in sec f64 a = 0.0; ///< Start mean anomaly in deg f64 n = 0.0; ///< Longitude of the ascending node in deg f64 p = 0.0; ///< Longitude of the periapsis in deg f64 i = 0.0; ///< Inclination in deg f64 RA = 0.0; ///< Right ascension relative to sol f64 dec = 0.0; ///< Declination relative to sol f64 dist = 0.0; ///< Distance from sol f64 td = 1.0; ///< Reference body period divisor f64 tf = 1.0; ///< Reference body period factor }; KEG_TYPE_DECL(SystemOrbitProperties); struct SystemBody { nString name = ""; nString parentName = ""; SystemBody* parent = nullptr; std::vector children; vecs::EntityID entity = 0; SpaceBodyType type = SpaceBodyType::NONE; SystemOrbitProperties properties; f64 mass = 0.0; bool isBaryCalculated = false; ///< Used by barycenters bool hasComputedRef = false; ///< True when it has computed trojan and t with ref body }; struct PlanetProperties { f64 diameter = 0.0; f64 density = 0.0; f64 mass = 0.0; f32 aTilt = 0.0; f32 lNorth = 0.0; f64 rotationalPeriod = 0.0; nString displayName = ""; nString generation = ""; PlanetGenData* planetGenData = nullptr; AtmosphereProperties atmosphere; CloudsProperties clouds; }; KEG_TYPE_DECL(PlanetProperties); struct StarProperties { f64 surfaceTemperature = 0.0; ///< temperature in kelvin f64 diameter = 0.0; f64 density = 0.0; f64 mass = 0.0; f32 aTilt = 0.0; f32 lNorth = 0.0; f64 rotationalPeriod = 0.0; nString displayName = ""; }; KEG_TYPE_DECL(StarProperties); struct GasGiantProperties { f64 diameter = 0.0; f64 density = 0.0; f64 mass = 0.0; f32 aTilt = 0.0; f32 lNorth = 0.0; f64 rotationalPeriod = 0.0; f32 oblateness = 0.0; nString colorMap = ""; nString displayName = ""; AtmosphereProperties atmosphere; Array rings; }; KEG_TYPE_DECL(GasGiantProperties); #endif // SpaceSystemLoadStructs_h__ ================================================ FILE: SoA/SpaceSystemLoader.cpp ================================================ #include "stdafx.h" #include "SpaceSystemLoader.h" #include "Constants.h" #include "Errors.h" #include "SoaOptions.h" #include "OrbitComponentUpdater.h" #include "PlanetGenData.h" #include "PlanetGenLoader.h" #include "ProgramGenDelegate.h" #include "SoAState.h" #include "SpaceSystemAssemblages.h" #include "SpaceSystemLoadStructs.h" #include #include #include #include #include #include void SpaceSystemLoader::init(const SoaState* soaState) { m_soaState = soaState; m_spaceSystem = soaState->spaceSystem; m_ioManager = soaState->systemIoManager; m_threadpool = soaState->threadPool; m_bodyLoader.init(m_ioManager); } void SpaceSystemLoader::loadStarSystem(const nString& path) { m_ioManager->setSearchDirectory((path + "/").c_str()); // Load the path color scheme loadPathColors(); // Load the system loadSystemProperties(); // Set up binary masses initBinaries(); // Set up parent connections and orbits initOrbits(); } // Only used in SoaEngine::loadPathColors struct PathColorKegProps { ui8v4 base = ui8v4(0); ui8v4 hover = ui8v4(0); }; KEG_TYPE_DEF_SAME_NAME(PathColorKegProps, kt) { KEG_TYPE_INIT_ADD_MEMBER(kt, PathColorKegProps, base, UI8_V4); KEG_TYPE_INIT_ADD_MEMBER(kt, PathColorKegProps, hover, UI8_V4); } bool SpaceSystemLoader::loadPathColors() { nString data; if (!m_ioManager->readFileToString("PathColors.yml", data)) { pError("Couldn't find " + m_ioManager->getSearchDirectory().getString() + "/PathColors.yml"); } keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { fprintf(stderr, "Failed to load %s\n", (m_ioManager->getSearchDirectory().getString() + "/PathColors.yml").c_str()); context.reader.dispose(); return false; } bool goodParse = true; auto f = makeFunctor([&](Sender, const nString& name, keg::Node value) { PathColorKegProps props; keg::Error err = keg::parse((ui8*)&props, value, context, &KEG_GLOBAL_TYPE(PathColorKegProps)); if (err != keg::Error::NONE) { fprintf(stderr, "Failed to parse node %s in PathColors.yml\n", name.c_str()); goodParse = false; } f32v4 base = f32v4(props.base) / 255.0f; f32v4 hover = f32v4(props.hover) / 255.0f; m_spaceSystem->pathColorMap[name] = std::make_pair(base, hover); }); context.reader.forAllInMap(node, &f); context.reader.dispose(); return goodParse; } bool SpaceSystemLoader::loadSystemProperties() { nString data; if (!m_ioManager->readFileToString("SystemProperties.yml", data)) { pError("Couldn't find " + m_ioManager->getSearchDirectory().getString() + "/SystemProperties.yml"); } keg::ReadContext context; context.env = keg::getGlobalEnvironment(); context.reader.init(data.c_str()); keg::Node node = context.reader.getFirst(); if (keg::getType(node) != keg::NodeType::MAP) { fprintf(stderr, "Failed to load %s\n", (m_ioManager->getSearchDirectory().getString() + "/SystemProperties.yml").c_str()); context.reader.dispose(); return false; } bool goodParse = true; auto f = makeFunctor([this, &goodParse, &context](Sender, const nString& name, keg::Node value) { // Parse based on the name if (name == "description") { m_spaceSystem->systemDescription = keg::convert(value); } else if (name == "age") { m_spaceSystem->age = keg::convert(value); } else { SystemOrbitProperties properties; keg::Error err = keg::parse((ui8*)&properties, value, context, &KEG_GLOBAL_TYPE(SystemOrbitProperties)); if (err != keg::Error::NONE) { fprintf(stderr, "Failed to parse node %s in SystemProperties.yml\n", name.c_str()); goodParse = false; } // Allocate the body SystemBody* body = new SystemBody; body->name = name; body->parentName = properties.par; body->properties = properties; if (properties.path.size()) { m_bodyLoader.loadBody(m_soaState, properties.path, &properties, body, value); m_bodyLookupMap[body->name] = body->entity; } else { // Make default orbit (used for barycenters) SpaceSystemAssemblages::createOrbit(m_spaceSystem, &properties, body, 0.0); } if (properties.type == SpaceObjectType::BARYCENTER) { m_barycenters[name] = body; } m_systemBodies[name] = body; } }); context.reader.forAllInMap(node, &f); context.reader.dispose(); return goodParse; } void SpaceSystemLoader::initBinaries() { for (auto& it : m_barycenters) { SystemBody* bary = it.second; initBinary(bary); } } void SpaceSystemLoader::initBinary(SystemBody* bary) { // Don't update twice if (bary->isBaryCalculated) return; bary->isBaryCalculated = true; // Need two components or its not a binary if (bary->properties.comps.size() != 2) return; // A component auto bodyA = m_systemBodies.find(std::string(bary->properties.comps[0])); if (bodyA == m_systemBodies.end()) return; auto& aProps = bodyA->second->properties; // B component auto bodyB = m_systemBodies.find(std::string(bary->properties.comps[1])); if (bodyB == m_systemBodies.end()) return; auto& bProps = bodyB->second->properties; { // Set orbit parameters relative to A component bProps.ref = bodyA->second->name; bProps.td = 1.0f; bProps.tf = 1.0f; bProps.e = aProps.e; bProps.i = aProps.i; bProps.n = aProps.n; bProps.p = aProps.p + 180.0; bProps.a = aProps.a; auto& oCmp = m_spaceSystem->orbit.getFromEntity(bodyB->second->entity); oCmp.e = bProps.e; oCmp.i = bProps.i * DEG_TO_RAD; oCmp.p = bProps.p * DEG_TO_RAD; oCmp.o = bProps.n * DEG_TO_RAD; oCmp.startMeanAnomaly = bProps.a * DEG_TO_RAD; } // Get the A mass auto& aSgCmp = m_spaceSystem->sphericalGravity.getFromEntity(bodyA->second->entity); f64 massA = aSgCmp.mass; // Recurse if child is a non-constructed binary if (massA == 0.0) { initBinary(bodyA->second); massA = aSgCmp.mass; } // Get the B mass auto& bSgCmp = m_spaceSystem->sphericalGravity.getFromEntity(bodyB->second->entity); f64 massB = bSgCmp.mass; // Recurse if child is a non-constructed binary if (massB == 0.0) { initBinary(bodyB->second); massB = bSgCmp.mass; } // Set the barycenter mass bary->mass = massA + massB; auto& barySgCmp = m_spaceSystem->sphericalGravity.getFromEntity(bary->entity); barySgCmp.mass = bary->mass; { // Calculate A orbit SystemBody* body = bodyA->second; body->parent = bary; bary->children.push_back(body); f64 massRatio = massB / (massA + massB); calculateOrbit(body->entity, barySgCmp.mass, body, massRatio); } { // Calculate B orbit SystemBody* body = bodyB->second; body->parent = bary; bary->children.push_back(body); f64 massRatio = massA / (massA + massB); calculateOrbit(body->entity, barySgCmp.mass, body, massRatio); } { // Set orbit colors from A component auto& oCmp = m_spaceSystem->orbit.getFromEntity(bodyA->second->entity); auto& baryOCmp = m_spaceSystem->orbit.getFromEntity(bary->entity); baryOCmp.pathColor[0] = oCmp.pathColor[0]; baryOCmp.pathColor[1] = oCmp.pathColor[1]; } } void recursiveInclinationCalc(OrbitComponentTable& ct, SystemBody* body, f64 inclination) { for (auto& c : body->children) { OrbitComponent& orbitC = ct.getFromEntity(c->entity); orbitC.i += inclination; recursiveInclinationCalc(ct, c, orbitC.i); } } void SpaceSystemLoader::initOrbits() { // Set parent connections for (auto& it : m_systemBodies) { SystemBody* body = it.second; const nString& parent = body->parentName; if (parent.length()) { // Check for parent auto p = m_systemBodies.find(parent); if (p != m_systemBodies.end()) { // Set up parent connection body->parent = p->second; p->second->children.push_back(body); } } } // Child propagation for inclination // TODO(Ben): Do this right /* for (auto& it : pr.systemBodies) { SystemBody* body = it.second; if (!body->parent) { recursiveInclinationCalc(pr.spaceSystem->m_orbitCT, body, pr.spaceSystem->m_orbitCT.getFromEntity(body->entity).i); } }*/ // Finally, calculate the orbits for (auto& it : m_systemBodies) { SystemBody* body = it.second; // Calculate the orbit using parent mass if (body->parent) { calculateOrbit(body->entity, m_spaceSystem->sphericalGravity.getFromEntity(body->parent->entity).mass, body); } } } void SpaceSystemLoader::computeRef(SystemBody* body) { if (!body->properties.ref.empty()) { OrbitComponent& orbitC = m_spaceSystem->orbit.getFromEntity(body->entity); // Find reference body auto it = m_systemBodies.find(body->properties.ref); if (it != m_systemBodies.end()) { SystemBody* ref = it->second; // Recursively compute ref if needed if (!ref->hasComputedRef) computeRef(ref); // Calculate period using reference body orbitC.t = ref->properties.t * body->properties.tf / body->properties.td; body->properties.t = orbitC.t; // Handle trojans if (body->properties.trojan == TrojanType::L4) { body->properties.a = ref->properties.a + 60.0f; orbitC.startMeanAnomaly = body->properties.a * DEG_TO_RAD; } else if (body->properties.trojan == TrojanType::L5) { body->properties.a = ref->properties.a - 60.0f; orbitC.startMeanAnomaly = body->properties.a * DEG_TO_RAD; } } else { fprintf(stderr, "Failed to find ref body %s\n", body->properties.ref.c_str()); } } body->hasComputedRef = true; } void SpaceSystemLoader::calculateOrbit(vecs::EntityID entity, f64 parentMass, SystemBody* body, f64 binaryMassRatio /* = 0.0 */) { OrbitComponent& orbitC = m_spaceSystem->orbit.getFromEntity(entity); // If the orbit was already calculated, don't do it again. if (orbitC.isCalculated) return; orbitC.isCalculated = true; // Provide the orbit component with it's parent m_spaceSystem->orbit.getFromEntity(body->entity).parentOrbId = m_spaceSystem->orbit.getComponentID(body->parent->entity); computeRef(body); f64 t = orbitC.t; auto& sgCmp = m_spaceSystem->sphericalGravity.getFromEntity(entity); f64 mass = sgCmp.mass; f64 diameter = sgCmp.radius * 2.0; if (binaryMassRatio > 0.0) { // Binary orbit orbitC.a = pow((t * t) * M_G * parentMass / (4.0 * (M_PI * M_PI)), 1.0 / 3.0) * KM_PER_M * binaryMassRatio; } else { // Regular orbit // Calculate semi-major axis orbitC.a = pow((t * t) * M_G * (mass + parentMass) / (4.0 * (M_PI * M_PI)), 1.0 / 3.0) * KM_PER_M; } // Calculate semi-minor axis orbitC.b = orbitC.a * sqrt(1.0 - orbitC.e * orbitC.e); // Set parent pass orbitC.parentMass = parentMass; // TODO(Ben): Doesn't work right for binaries due to parentMass { // Check tidal lock f64 ns = log10(0.003 * pow(orbitC.a, 6.0) * pow(diameter + 500.0, 3.0) / (mass * orbitC.parentMass) * (1.0 + (f64)1e20 / (mass + orbitC.parentMass))); if (ns < 0) { // It is tidally locked so lock the rotational period m_spaceSystem->axisRotation.getFromEntity(entity).period = t; } } { // Make the ellipse mesh with stepwise simulation OrbitComponentUpdater updater; static const int NUM_VERTS = 2880; orbitC.verts.resize(NUM_VERTS + 1); f64 timePerDeg = orbitC.t / (f64)NUM_VERTS; NamePositionComponent& npCmp = m_spaceSystem->namePosition.get(orbitC.npID); f64v3 startPos = npCmp.position; for (int i = 0; i < NUM_VERTS; i++) { if (orbitC.parentOrbId) { OrbitComponent* pOrbC = &m_spaceSystem->orbit.get(orbitC.parentOrbId); updater.updatePosition(orbitC, i * timePerDeg, &npCmp, pOrbC, &m_spaceSystem->namePosition.get(pOrbC->npID)); } else { updater.updatePosition(orbitC, i * timePerDeg, &npCmp); } OrbitComponent::Vertex vert; vert.position = npCmp.position; vert.angle = 1.0f - (f32)i / (f32)NUM_VERTS; orbitC.verts[i] = vert; } orbitC.verts.back() = orbitC.verts.front(); npCmp.position = startPos; } } ================================================ FILE: SoA/SpaceSystemLoader.h ================================================ /// /// SpaceSystemLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 4 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Loads the space system /// #pragma once #ifndef SpaceSystemLoader_h__ #define SpaceSystemLoader_h__ #include #include #include "VoxPool.h" #include "SystemBodyLoader.h" struct GasGiantProperties; struct SystemBody; struct SystemOrbitProperties; class SpaceSystem; class PlanetGenLoader; DECL_VIO(class IOManager) DECL_VCORE(class RPCManager) class SpaceSystemLoader { public: void init(const SoaState* soaState); /// Loads and adds a star system to the SpaceSystem /// @param pr: params void loadStarSystem(const nString& path); private: /// Loads path color scheme /// @param pr: params /// @return true on success bool loadPathColors(); /// Loads and adds system properties to the params /// @param pr: params /// @return true on success bool loadSystemProperties(); // Sets up mass parameters for binaries void initBinaries(); // Recursive function for binary creation void initBinary(SystemBody* bary); // Initializes orbits and parent connections void initOrbits(); void computeRef(SystemBody* body); void calculateOrbit(vecs::EntityID entity, f64 parentMass, SystemBody* body, f64 binaryMassRatio = 0.0); SystemBodyLoader m_bodyLoader; const SoaState* m_soaState = nullptr; SpaceSystem* m_spaceSystem; vio::IOManager* m_ioManager = nullptr; vcore::ThreadPool* m_threadpool = nullptr; std::map m_barycenters; std::map m_systemBodies; std::map m_bodyLookupMap; }; #endif // SpaceSystemLoader_h__ ================================================ FILE: SoA/SpaceSystemRenderStage.cpp ================================================ #include "stdafx.h" #include "SpaceSystem.h" #include "SpaceSystemRenderStage.h" #include #include #include "App.h" #include "Camera.h" #include "DebugRenderer.h" #include "Errors.h" #include "MTRenderState.h" #include "MainMenuSystemViewer.h" #include "RenderUtils.h" #include "SoAState.h" #include "SpaceSystemComponents.h" #include "TerrainPatch.h" #include "TerrainPatchMeshManager.h" #include "soaUtils.h" #include #define ATMO_LOAD_DIST 50000.0f /* ===================== LoadContext =================== */ const ui32 LENS_WORK = 2; const ui32 STAR_WORK = 2; const ui32 AR_WORK = 2; const ui32 SPHER_WORK = 2; const ui32 GAS_WORK = 2; const ui32 CLOUD_WORK = 2; const ui32 ATMO_WORK = 2; const ui32 RING_WORK = 2; const ui32 FAR_WORK = 2; // Make sure to sum all work here const ui32 TOTAL_WORK = LENS_WORK + STAR_WORK + AR_WORK + SPHER_WORK + GAS_WORK + CLOUD_WORK + ATMO_WORK + RING_WORK + FAR_WORK; // Make sure NUM_TASKS matches the number of tasks listed const ui32 NUM_TASKS = 9; /* ====================================================== */ const f64q FACE_ORIENTATIONS[6] = { f64q(f64v3(0.0, 0.0, 0.0)), // TOP f64q(f64v3(0.0, 0.0, -M_PI / 2.0)), // LEFT f64q(f64v3(0.0, 0.0, M_PI / 2.0)), // RIGHT f64q(f64v3(M_PI / 2.0, 0.0, 0.0)), // FRONT f64q(f64v3(-M_PI / 2.0, 0.0, 0.0)), // BACK f64q(f64v3(M_PI, 0.0, 0.0)) // BOTTOM }; void SpaceSystemRenderStage::init(vui::GameWindow* window, StaticLoadContext& context) { IRenderStage::init(window, context); context.addAnticipatedWork(TOTAL_WORK, NUM_TASKS); } void SpaceSystemRenderStage::hook(SoaState* state, const Camera* spaceCamera, const Camera* farTerrainCamera /*= nullptr*/) { m_viewport = m_window->getViewportDims(); m_spaceSystem = state->spaceSystem; m_mainMenuSystemViewer = state->clientState.systemViewer; m_lensFlareRenderer.init(&state->clientState.texturePathResolver); m_starRenderer.init(&state->clientState.texturePathResolver); m_systemARRenderer.init(&state->clientState.texturePathResolver); m_spaceCamera = spaceCamera; m_farTerrainCamera = farTerrainCamera; } void SpaceSystemRenderStage::load(StaticLoadContext& context) { context.addTask([&](Sender, void*) { m_lensFlareRenderer.initGL(); context.addWorkCompleted(LENS_WORK); }, false); context.addTask([&](Sender, void*) { m_starRenderer.initGL(); context.addWorkCompleted(STAR_WORK); }, false); context.addTask([&](Sender, void*) { m_systemARRenderer.initGL(); context.addWorkCompleted(AR_WORK); }, false); context.addTask([&](Sender, void*) { m_sphericalTerrainComponentRenderer.initGL(); context.addWorkCompleted(SPHER_WORK); }, false); context.addTask([&](Sender, void*) { m_gasGiantComponentRenderer.initGL(); context.addWorkCompleted(GAS_WORK); }, false); context.addTask([&](Sender, void*) { m_cloudsComponentRenderer.initGL(); context.addWorkCompleted(CLOUD_WORK); }, false); context.addTask([&](Sender, void*) { m_atmosphereComponentRenderer.initGL(); context.addWorkCompleted(ATMO_WORK); }, false); context.addTask([&](Sender, void*) { m_ringsRenderer.initGL(); context.addWorkCompleted(RING_WORK); }, false); context.addTask([&](Sender, void*) { m_farTerrainComponentRenderer.initGL(); context.addWorkCompleted(LENS_WORK); }, false); } void SpaceSystemRenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { m_lensFlareRenderer.dispose(); m_starRenderer.dispose(); m_systemARRenderer.dispose(); m_sphericalTerrainComponentRenderer.dispose(); m_gasGiantComponentRenderer.dispose(); m_cloudsComponentRenderer.dispose(); m_atmosphereComponentRenderer.dispose(); m_ringsRenderer.dispose(); m_farTerrainComponentRenderer.dispose(); } void SpaceSystemRenderStage::setRenderState(const MTRenderState* renderState) { m_renderState = renderState; } void SpaceSystemRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { drawBodies(); if (m_showAR) m_systemARRenderer.draw(m_spaceSystem, m_spaceCamera, m_mainMenuSystemViewer, m_viewport); } void SpaceSystemRenderStage::reloadShaders() { StaticLoadContext tmp; dispose(tmp); m_lensFlareRenderer.initGL(); m_starRenderer.initGL(); m_systemARRenderer.initGL(); m_sphericalTerrainComponentRenderer.initGL(); m_gasGiantComponentRenderer.initGL(); m_cloudsComponentRenderer.initGL(); m_atmosphereComponentRenderer.initGL(); m_ringsRenderer.initGL(); m_farTerrainComponentRenderer.initGL(); } void SpaceSystemRenderStage::renderStarGlows(const f32v3& colorMult) { for (auto& it : m_starGlowsToRender) { m_starRenderer.drawGlow(it.first, m_spaceCamera->getViewProjectionMatrix(), it.second, m_spaceCamera->getAspectRatio(), m_spaceCamera->getDirection(), m_spaceCamera->getRight(), colorMult); // TODO(Ben): Don't do this twice? f32v3 starColor = m_starRenderer.calculateStarColor(it.first); f32 intensity = (f32)glm::min(m_starRenderer.calculateGlowSize(it.first, it.second), 1.0) * it.first.visibility; m_lensFlareRenderer.render(m_spaceCamera->getViewProjectionMatrix(), it.second, starColor * colorMult, m_spaceCamera->getAspectRatio(), 0.1f, intensity); } } void SpaceSystemRenderStage::drawBodies() { glEnable(GL_DEPTH_CLAMP); bool needsDepthClear = false; // TODO(Ben): Optimize out getFromEntity f64v3 lightPos; // For caching light for far terrain std::map > lightCache; const f64v3* pos; f32 zCoef = computeZCoef(m_spaceCamera->getFarClip()); // Render spherical terrain for (auto& it : m_spaceSystem->sphericalTerrain) { auto& cmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(cmp.namePositionComponent); // Cant render if it hasn't generated yet if (!cmp.planetGenData) continue; // Indicate the need for face transition animation if (cmp.needsFaceTransitionAnimation) { cmp.needsFaceTransitionAnimation = false; needsFaceTransitionAnimation = true; } // If we are using MTRenderState, get position from it pos = getBodyPosition(npCmp, it.first); // Need to clear depth on fade transitions if (cmp.farTerrainComponent && (cmp.alpha > 0.0f && cmp.alpha < TERRAIN_FADE_LENGTH)) { needsDepthClear = true; } SpaceLightComponent* lightCmp = getBrightestLight(npCmp, lightPos); lightCache[it.first] = std::make_pair(lightPos, lightCmp); f32v3 lightDir(glm::normalize(lightPos - *pos)); m_sphericalTerrainComponentRenderer.draw(cmp, m_spaceCamera, lightDir, *pos, zCoef, lightCmp, &m_spaceSystem->axisRotation.get(cmp.axisRotationComponent), &m_spaceSystem->atmosphere.getFromEntity(it.first)); } // Render gas giants for (auto& it : m_spaceSystem->gasGiant) { auto& ggCmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(ggCmp.namePositionComponent); pos = getBodyPosition(npCmp, it.first); f32v3 relCamPos(m_spaceCamera->getPosition() - *pos); SpaceLightComponent* lightCmp = getBrightestLight(npCmp, lightPos); lightCache[it.first] = std::make_pair(lightPos, lightCmp); f32v3 lightDir(glm::normalize(lightPos - *pos)); m_gasGiantComponentRenderer.draw(ggCmp, it.first, m_spaceCamera->getViewProjectionMatrix(), m_spaceSystem->axisRotation.getFromEntity(it.first).currentOrientation, relCamPos, lightDir, zCoef, lightCmp, &m_spaceSystem->atmosphere.getFromEntity(it.first)); } // Render clouds /* glDisable(GL_CULL_FACE); for (auto& it : m_spaceSystem->m_cloudsCT) { auto& cCmp = it.second; auto& npCmp = m_spaceSystem->m_namePositionCT.get(cCmp.namePositionComponent); pos = getBodyPosition(npCmp, it.first); f32v3 relCamPos(m_spaceCamera->getPosition() - *pos); auto& l = lightCache[it.first]; f32v3 lightDir(glm::normalize(l.first - *pos)); m_cloudsComponentRenderer.draw(cCmp, m_spaceCamera->getViewProjectionMatrix(), relCamPos, lightDir, zCoef, l.second, m_spaceSystem->m_axisRotationCT.getFromEntity(it.first), m_spaceSystem->m_atmosphereCT.getFromEntity(it.first)); } glEnable(GL_CULL_FACE);*/ // Render atmospheres for (auto& it : m_spaceSystem->atmosphere) { auto& atCmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(atCmp.namePositionComponent); pos = getBodyPosition(npCmp, it.first); f32v3 relCamPos(m_spaceCamera->getPosition() - *pos); if (glm::length(relCamPos) < atCmp.radius * 11.0f) { auto& l = lightCache[it.first]; f32v3 lightDir(glm::normalize(l.first - *pos)); m_atmosphereComponentRenderer.draw(atCmp, m_spaceCamera->getViewProjectionMatrix(), relCamPos, lightDir, zCoef, l.second); } } // Render planet rings glDepthMask(GL_FALSE); for (auto& it : m_spaceSystem->planetRings) { auto& prCmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(prCmp.namePositionComponent); // TODO(Ben): Don't use getFromEntity auto& sgCmp = m_spaceSystem->sphericalGravity.getFromEntity(it.first); pos = getBodyPosition(npCmp, it.first); f32v3 relCamPos(m_spaceCamera->getPosition() - *pos); auto& l = lightCache[it.first]; f32v3 lightDir(glm::normalize(l.first - *pos)); // TODO(Ben): Worry about f64 to f32 precision loss m_ringsRenderer.draw(prCmp, it.first, m_spaceCamera->getViewProjectionMatrix(), relCamPos, f32v3(l.first - m_spaceCamera->getPosition()), (f32)sgCmp.radius, zCoef, l.second); } glDepthMask(GL_TRUE); // Render far terrain if (m_farTerrainCamera) { if (needsDepthClear) { glClear(GL_DEPTH_BUFFER_BIT); } for (auto& it : m_spaceSystem->farTerrain) { auto& cmp = it.second; auto& npCmp = m_spaceSystem->namePosition.getFromEntity(it.first); if (!cmp.meshManager) continue; pos = getBodyPosition(npCmp, it.first); auto& l = lightCache[it.first]; f64v3 lightDir = glm::normalize(l.first - *pos); m_farTerrainComponentRenderer.draw(cmp, m_farTerrainCamera, lightDir, zCoef, l.second, &m_spaceSystem->axisRotation.getFromEntity(it.first), &m_spaceSystem->atmosphere.getFromEntity(it.first)); } } // Render stars m_starGlowsToRender.clear(); for (auto& it : m_spaceSystem->star) { auto& sCmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(sCmp.namePositionComponent); pos = getBodyPosition(npCmp, it.first); f64v3 relCamPos = m_spaceCamera->getPosition() - *pos; f32v3 fRelCamPos(relCamPos); // Render the star m_starRenderer.updateOcclusionQuery(sCmp, zCoef, m_spaceCamera->getViewProjectionMatrix(), relCamPos); m_starRenderer.drawStar(sCmp, m_spaceCamera->getViewProjectionMatrix(), f64q(), fRelCamPos, zCoef); m_starRenderer.drawCorona(sCmp, m_spaceCamera->getViewProjectionMatrix(), m_spaceCamera->getViewMatrix(), fRelCamPos, zCoef); m_starGlowsToRender.emplace_back(sCmp, relCamPos); } glDisable(GL_DEPTH_CLAMP); vg::DepthState::FULL.set(); } SpaceLightComponent* SpaceSystemRenderStage::getBrightestLight(NamePositionComponent& npCmp, OUT f64v3& pos) { SpaceLightComponent* rv = nullptr; f64 closestDist = 999999999999999999999999999999999999999999999.0; for (auto& it : m_spaceSystem->spaceLight) { auto& lightNpCmp = m_spaceSystem->namePosition.get(it.second.npID); f64 dist = selfDot(lightNpCmp.position - npCmp.position); if (dist < closestDist) { closestDist = dist; rv = &it.second; pos = lightNpCmp.position; } } return rv; } const f64v3* SpaceSystemRenderStage::getBodyPosition(NamePositionComponent& npCmp, vecs::EntityID eid) { const f64v3* pos; // If we are using MTRenderState, get position from it if (m_renderState) { auto sit = m_renderState->spaceBodyPositions.find(eid); if (sit != m_renderState->spaceBodyPositions.end()) { pos = &sit->second; } else { pos = &npCmp.position; } } else { pos = &npCmp.position; } return pos; } ================================================ FILE: SoA/SpaceSystemRenderStage.h ================================================ /// /// SpaceSystemRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 5 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Render stage for drawing the space system /// #pragma once #ifndef SpaceSystemRenderStage_h__ #define SpaceSystemRenderStage_h__ #include #include #include #include "CloudsComponentRenderer.h" #include "AtmosphereComponentRenderer.h" #include "Camera.h" #include "FarTerrainComponentRenderer.h" #include "GasGiantComponentRenderer.h" #include "LenseFlareRenderer.h" #include "PlanetRingsComponentRenderer.h" #include "SpaceSystemComponents.h" #include "SphericalTerrainComponentRenderer.h" #include "StarComponentRenderer.h" #include "SystemARRenderer.h" #include "IRenderStage.h" class App; class GameSystem; class MainMenuSystemViewer; struct SoaState; class SpaceSystem; class SpriteBatch; class SpriteFont; struct MTRenderState; class SpaceSystemRenderStage : public IRenderStage { public: void init(vui::GameWindow* window, StaticLoadContext& context) override; void hook(SoaState* state, const Camera* spaceCamera, const Camera* farTerrainCamera = nullptr); void load(StaticLoadContext& context) override; void dispose(StaticLoadContext& context) override; //TODO(Ben): Pointer to window viewport instead? void setViewport(const ui32v2& viewport) { m_viewport = f32v2(viewport); } void setFarTerrainCamera(const Camera* camera) { m_farTerrainCamera = camera; } /// Call this every frame before render void setRenderState(const MTRenderState* renderState); /// Draws the render stage virtual void render(const Camera* camera) override; void reloadShaders(); /// Renders star glows requested in the render call. Call after HDR void renderStarGlows(const f32v3& colorMult); void setSystemViewer(const MainMenuSystemViewer* viewer) { m_mainMenuSystemViewer = viewer; } void setShowAR(bool showAR) { m_showAR = showAR; } bool needsFaceTransitionAnimation = false; ///< true when we need to fade out camera for transition between faces private: /// Renders the space bodies /// @param camera: Camera for rendering /// @param terrainProgram: Program for rendering terrain /// @param waterProgram: Program for rendering water void drawBodies(); /// Gets light source relative to a component /// @param cmp: position component /// @param pos: Returned position of brightest light /// @return brightest light source relative to cmp SpaceLightComponent* getBrightestLight(NamePositionComponent& npCmp, OUT f64v3& pos); /// Gets the position of a body using MTRenderState if needed /// @return pointer to the position const f64v3* getBodyPosition(NamePositionComponent& npCmp, vecs::EntityID eid); f32v2 m_viewport; SpaceSystem* m_spaceSystem = nullptr; const MainMenuSystemViewer* m_mainMenuSystemViewer = nullptr; const Camera* m_spaceCamera = nullptr; const Camera* m_farTerrainCamera = nullptr; const MTRenderState* m_renderState = nullptr; CloudsComponentRenderer m_cloudsComponentRenderer; AtmosphereComponentRenderer m_atmosphereComponentRenderer; PlanetRingsComponentRenderer m_ringsRenderer; FarTerrainComponentRenderer m_farTerrainComponentRenderer; GasGiantComponentRenderer m_gasGiantComponentRenderer; LenseFlareRenderer m_lensFlareRenderer; SphericalTerrainComponentRenderer m_sphericalTerrainComponentRenderer; StarComponentRenderer m_starRenderer; SystemARRenderer m_systemARRenderer; std::vector > m_starGlowsToRender; bool m_showAR = true; vcore::GLRPC m_rpc; }; #endif // SpaceSystemRenderStage_h__ ================================================ FILE: SoA/SpaceSystemUpdater.cpp ================================================ #include "stdafx.h" #include "SpaceSystemUpdater.h" #include "SoAState.h" #include void SpaceSystemUpdater::init(const SoaState* soaState) { // Set planet rotation m_axisRotationComponentUpdater.update(soaState->spaceSystem, soaState->time); // Set initial position m_orbitComponentUpdater.update(soaState->spaceSystem, soaState->time); } void SpaceSystemUpdater::update(SoaState* soaState, const f64v3& spacePos, const f64v3& voxelPos) { // Get handles SpaceSystem* spaceSystem = soaState->spaceSystem; // const GameSystem* gameSystem = soaState->gameSystem; // Update planet rotation m_axisRotationComponentUpdater.update(spaceSystem, soaState->time); // Update far terrain // Update this BEFORE sphericalTerrain m_farTerrainComponentUpdater.update(spaceSystem, voxelPos * KM_PER_VOXEL); // Update Spherical Terrain m_sphericalTerrainComponentUpdater.update(soaState, spacePos); // Update voxels m_sphericalVoxelComponentUpdater.update(soaState); // Update Orbits ( Do this last) m_orbitComponentUpdater.update(spaceSystem, soaState->time); } void SpaceSystemUpdater::glUpdate(const SoaState* soaState) { m_sphericalTerrainComponentUpdater.glUpdate(soaState); m_farTerrainComponentUpdater.glUpdate(soaState->spaceSystem); } ================================================ FILE: SoA/SpaceSystemUpdater.h ================================================ /// /// SpaceSystemUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 20 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates the SpaceSystem /// #pragma once #ifndef SpaceSystemUpdater_h__ #define SpaceSystemUpdater_h__ #include "AxisRotationComponentUpdater.h" #include "FarTerrainComponentUpdater.h" #include "OrbitComponentUpdater.h" #include "SphericalTerrainComponentUpdater.h" #include "SphericalVoxelComponentUpdater.h" class SpaceSystem; class GameSystem; struct SoaState; class SpaceSystemUpdater { public: void init(const SoaState* soaState); void update(SoaState* soaState, const f64v3& spacePos, const f64v3& voxelPos); /// Updates OpenGL specific stuff, should be called on render thread void glUpdate(const SoaState* soaState); private: /// Updaters friend class OrbitComponentUpdater; OrbitComponentUpdater m_orbitComponentUpdater; friend class SphericalVoxelComponentUpdater; SphericalVoxelComponentUpdater m_sphericalVoxelComponentUpdater; friend class SphericalTerrainComponentUpdater; SphericalTerrainComponentUpdater m_sphericalTerrainComponentUpdater; friend class AxisRotationComponentUpdater; AxisRotationComponentUpdater m_axisRotationComponentUpdater; FarTerrainComponentUpdater m_farTerrainComponentUpdater; }; #endif // SpaceSystemUpdater_h__ ================================================ FILE: SoA/SphericalHeightmapGenerator.cpp ================================================ #include "stdafx.h" #include "SphericalHeightmapGenerator.h" #include "PlanetHeightData.h" #include "VoxelSpaceConversions.h" #include "Noise.h" #include "soaUtils.h" #include #define WEIGHT_THRESHOLD 0.001 void SphericalHeightmapGenerator::init(const PlanetGenData* planetGenData) { m_genData = planetGenData; } void SphericalHeightmapGenerator::generateHeightData(OUT PlanetHeightData& height, const VoxelPosition2D& facePosition) const { // Need to convert to world-space f32v2 coordMults = f32v2(VoxelSpaceConversions::FACE_TO_WORLD_MULTS[(int)facePosition.face]); i32v3 coordMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)facePosition.face]; f64v3 pos; pos[coordMapping.x] = facePosition.pos.x * KM_PER_VOXEL * coordMults.x; pos[coordMapping.y] = m_genData->radius * (f64)VoxelSpaceConversions::FACE_Y_MULTS[(int)facePosition.face]; pos[coordMapping.z] = facePosition.pos.y * KM_PER_VOXEL * coordMults.y; f64v3 normal = glm::normalize(pos); generateHeightData(height, normal * m_genData->radius, normal); // For Voxel Position, automatically get tree or flora height.flora = getTreeID(height.biome, facePosition, pos); // If no tree, try flora if (height.flora == FLORA_ID_NONE) { height.flora = getFloraID(height.biome, facePosition, pos); } } void SphericalHeightmapGenerator::generateHeightData(OUT PlanetHeightData& height, const f64v3& normal) const { generateHeightData(height, normal * m_genData->radius, normal); } FloraID SphericalHeightmapGenerator::getTreeID(const Biome* biome, const VoxelPosition2D& facePosition, const f64v3& worldPos) const { // TODO(Ben): Experiment with optimizations with large amounts of flora. f64 noTreeChance = 1.0; f64 totalChance = 0.0; // NOTE: Stack overflow bad mkay f64* chances = (f64*)alloca(sizeof(f64) * biome->trees.size()); // Determine chance for (size_t i = 0; i < biome->trees.size(); i++) { auto& t = biome->trees[i]; f64 c = t.chance.base; getNoiseValue(worldPos, t.chance.funcs, nullptr, TerrainOp::ADD, c); totalChance += c; chances[i] = totalChance; noTreeChance *= (1.0 - c); } // ITS SLOW std::hash h; std::uniform_real_distribution dist(0.0, 1.0); std::mt19937 slowRGen(h(facePosition.pos.x) ^ (h(facePosition.pos.y) << 1)); f64 r = dist(slowRGen); if (r < 1.0 - noTreeChance) { // A plant exists, now we determine which one // TODO(Ben): Binary search? f64 roll = dist(slowRGen) * totalChance; for (size_t i = 0; i < biome->trees.size(); i++) { if (roll <= chances[i]) { return biome->trees[i].id; } } } return FLORA_ID_NONE; } FloraID SphericalHeightmapGenerator::getFloraID(const Biome* biome, const VoxelPosition2D& facePosition, const f64v3& worldPos) const { // TODO(Ben): Experiment with optimizations with large amounts of flora. f64 noFloraChance = 1.0; f64 totalChance = 0.0; // NOTE: Stack overflow bad mkay f64* chances = (f64*)alloca(sizeof(f64) * biome->flora.size()); // Determine chance for (size_t i = 0; i < biome->flora.size(); i++) { auto& t = biome->flora[i]; f64 c = t.chance.base; getNoiseValue(worldPos, t.chance.funcs, nullptr, TerrainOp::ADD, c); totalChance += c; chances[i] = totalChance; noFloraChance *= (1.0 - c); } // Start random generator // ITS SLOW std::hash h; std::uniform_real_distribution dist(0.0, 1.0); std::mt19937 slowRGen(h(facePosition.pos.x) ^ (h(facePosition.pos.y) << 1)); f64 r = dist(slowRGen); if (r < 1.0 - noFloraChance) { f64 roll = dist(slowRGen) * totalChance; for (size_t i = 0; i < biome->flora.size(); i++) { if (roll <= chances[i]) { return biome->flora[i].id; } } } return FLORA_ID_NONE; } void getBaseBiomes(const std::vector baseBiomeInfluenceMap[BIOME_MAP_WIDTH][BIOME_MAP_WIDTH], f64 x, f64 y, OUT std::map& rvBiomes) { int ix = (int)x; int iy = (int)y; //0 1 //2 3 /* Interpolate */ // Get weights f64 fx = x - (f64)ix; f64 fy = y - (f64)iy; f64 fx1 = 1.0 - fx; f64 fy1 = 1.0 - fy; f64 w0 = fx1 * fy1; f64 w1 = fx * fy1; f64 w2 = fx1 * fy; f64 w3 = fx * fy; // Shorter handles #define BLIST_0 baseBiomeInfluenceMap[iy][ix] #define BLIST_1 baseBiomeInfluenceMap[iy][ix + 1] #define BLIST_2 baseBiomeInfluenceMap[iy + 1][ix] #define BLIST_3 baseBiomeInfluenceMap[iy + 1][ix + 1] // TODO(Ben): Explore padding to ditch ifs? /* Construct list of biomes to generate and assign weights from interpolation. */ // Top Left for (auto& b : BLIST_0) { auto it = rvBiomes.find(b); if (it == rvBiomes.end()) { rvBiomes[b] = w0 * b.weight; } else { it->second += w0 * b.weight; } } // Top Right if (ix < BIOME_MAP_WIDTH - 1) { for (auto& b : BLIST_1) { auto it = rvBiomes.find(b); if (it == rvBiomes.end()) { rvBiomes[b] = w1 * b.weight; } else { it->second += w1 * b.weight; } } } else { for (auto& b : BLIST_0) { rvBiomes[b] += w1 * b.weight; } } // Bottom left if (iy < BIOME_MAP_WIDTH - 1) { for (auto& b : BLIST_2) { auto it = rvBiomes.find(b); if (it == rvBiomes.end()) { rvBiomes[b] = w2 * b.weight; } else { it->second += w2 * b.weight; } } // Bottom right if (ix < BIOME_MAP_WIDTH - 1) { for (auto& b : BLIST_3) { auto it = rvBiomes.find(b); if (it == rvBiomes.end()) { rvBiomes[b] = w3 * b.weight; } else { it->second += w3 * b.weight; } } } else { for (auto& b : BLIST_2) { rvBiomes[b] += w3 * b.weight; } } } else { for (auto& b : BLIST_0) { rvBiomes[b] += w2 * b.weight; } for (auto& b : BLIST_1) { rvBiomes[b] += w3 * b.weight; } } } inline void SphericalHeightmapGenerator::generateHeightData(OUT PlanetHeightData& height, const f64v3& pos, const f64v3& normal) const { f64 h = getBaseHeightValue(pos); height.height = (f32)(h * VOXELS_PER_M); h *= KM_PER_M; f64 temperature = getTemperatureValue(pos, normal, h); f64 humidity = getHumidityValue(pos, normal, h); height.temperature = (ui8)temperature; height.humidity = (ui8)humidity; height.flora = FLORA_ID_NONE; // Base Biome f64 biggestWeight = 0.0; const Biome* bestBiome = m_genData->baseBiomeLookup[height.humidity][height.temperature]; std::map baseBiomes; getBaseBiomes(m_genData->baseBiomeInfluenceMap, temperature, humidity, baseBiomes); for (auto& bb : baseBiomes) { const Biome* biome = bb.first.b; f64 baseWeight = bb.first.weight * bb.second; // Get base biome terrain f64 newHeight = biome->terrainNoise.base + height.height; getNoiseValue(pos, biome->terrainNoise.funcs, nullptr, TerrainOp::ADD, newHeight); // Mix in height with squared interpolation height.height = (f32)((baseWeight * newHeight) + (1.0 - baseWeight) * (f64)height.height); // Sub biomes recurseChildBiomes(biome, pos, height.height, biggestWeight, bestBiome, baseWeight); } // Mark biome that is the best height.biome = bestBiome; } void SphericalHeightmapGenerator::recurseChildBiomes(const Biome* biome, const f64v3& pos, f32& height, f64& biggestWeight, const Biome*& bestBiome, f64 baseWeight) const { // Get child noise value f64 noiseVal = biome->childNoise.base; getNoiseValue(pos, biome->childNoise.funcs, nullptr, TerrainOp::ADD, noiseVal); // Sub biomes for (auto& child : biome->children) { f64 weight = 1.0; // Check if biome is present and get weight { // Noise f64 dx = noiseVal - child->noiseRange.x; f64 dy = child->noiseRange.y - noiseVal; // See which side we are closer to if (ABS(dx) < ABS(dy)) { if (dx < 0) { continue; } dx *= child->noiseScale.x; if (dx > 1.0) { dx = 1.0; } else { weight *= dx; } } else { if (dy < 0) { continue; } dy *= child->noiseScale.x; if (dy > 1.0) { dy = 1.0; } else { weight *= dy; } } } { // Height f64 dx = height - child->heightRange.x; f64 dy = child->heightRange.y - height; // See which side we are closer to if (ABS(dx) < ABS(dy)) { if (dx < 0) { continue; } dx *= child->heightScale.x; if (dx > 1.0) { dx = 1.0; } else { weight *= hermite(dx); } } else { if (dy < 0) { continue; } dy *= child->heightScale.x; if (dy > 1.0) { dy = 1.0; } else { weight *= hermite(dy); } } } // If we reach here, the biome exists. f64 newHeight = child->terrainNoise.base + height; getNoiseValue(pos, child->terrainNoise.funcs, nullptr, TerrainOp::ADD, newHeight); // Biggest weight biome is the next biome if (weight >= biggestWeight) { biggestWeight = weight; bestBiome = child; } weight = baseWeight * weight * weight; // Mix in height with squared interpolation height = (f32)((weight * newHeight) + (1.0 - weight) * height); // Recurse children if (child->children.size() && weight > WEIGHT_THRESHOLD) { recurseChildBiomes(child, pos, height, biggestWeight, bestBiome, weight); } } } f64 SphericalHeightmapGenerator::getBaseHeightValue(const f64v3& pos) const { f64 genHeight = m_genData->baseTerrainFuncs.base; getNoiseValue(pos, m_genData->baseTerrainFuncs.funcs, nullptr, TerrainOp::ADD, genHeight); return genHeight; } f64 SphericalHeightmapGenerator::getTemperatureValue(const f64v3& pos, const f64v3& normal, f64 height) const { f64 genHeight = m_genData->tempTerrainFuncs.base; getNoiseValue(pos, m_genData->tempTerrainFuncs.funcs, nullptr, TerrainOp::ADD, genHeight); return calculateTemperature(m_genData->tempLatitudeFalloff, computeAngleFromNormal(normal), genHeight - glm::max(0.0, m_genData->tempHeightFalloff * height)); } f64 SphericalHeightmapGenerator::getHumidityValue(const f64v3& pos, const f64v3& normal, f64 height) const { f64 genHeight = m_genData->humTerrainFuncs.base; getNoiseValue(pos, m_genData->humTerrainFuncs.funcs, nullptr, TerrainOp::ADD, genHeight); return SphericalHeightmapGenerator::calculateHumidity(m_genData->humLatitudeFalloff, computeAngleFromNormal(normal), genHeight - glm::max(0.0, m_genData->humHeightFalloff * height)); } // Thanks to tetryds for these f64 SphericalHeightmapGenerator::calculateTemperature(f64 range, f64 angle, f64 baseTemp) { f64 tempFalloff = 1.0 - pow(cos(angle), 2.0 * angle); if(glm::isnan(tempFalloff)) tempFalloff=0.0; f64 temp = baseTemp - tempFalloff * range; return glm::clamp(temp, 0.0, 255.0); } // Thanks to tetryds for these f64 SphericalHeightmapGenerator::calculateHumidity(f64 range, f64 angle, f64 baseHum) { f64 cos3x = cos(3.0 * angle); f64 humFalloff = 1.0 - (-0.25 * angle + 1.0) * (cos3x * cos3x); f64 hum = baseHum - humFalloff * range; return glm::clamp(hum, 0.0, 255.0); } f64 SphericalHeightmapGenerator::computeAngleFromNormal(const f64v3& normal) { // Compute angle if (abs(normal.y) <= 0.000000001) { // Need to do this to fix an equator bug return 0.0; } else { f64v3 equator = glm::normalize(f64v3(normal.x, 0.000000001, normal.z)); return acos(glm::dot(equator, normal)); } } f64 doOperation(const TerrainOp& op, f64 a, f64 b) { switch (op) { case TerrainOp::ADD: return a + b; case TerrainOp::SUB: return a - b; case TerrainOp::MUL: return a * b; case TerrainOp::DIV: return a / b; } return 0.0; } void SphericalHeightmapGenerator::getNoiseValue(const f64v3& pos, const Array& funcs, f64* modifier, const TerrainOp& op, f64& height) const { // NOTE: Make sure this implementation matches NoiseShaderGenerator::addNoiseFunctions() for (size_t f = 0; f < funcs.size(); ++f) { auto& fn = funcs[f]; f64 h = 0.0; f64* nextMod; TerrainOp nextOp; // Check if its not a noise function if (fn.func == TerrainStage::CONSTANT) { nextMod = &h; h = fn.low; // Apply parent before clamping if (modifier) { h = doOperation(op, h, *modifier); } // Optional clamp if both fields are not 0.0 if (fn.clamp[0] != 0.0 || fn.clamp[1] != 0.0) { h = glm::clamp(*modifier, (f64)fn.clamp[0], (f64)fn.clamp[1]); } nextOp = fn.op; } else if (fn.func == TerrainStage::PASS_THROUGH) { nextMod = modifier; // Apply parent before clamping if (modifier) { h = doOperation(op, *modifier, fn.low); // Optional clamp if both fields are not 0.0 if (fn.clamp[0] != 0.0 || fn.clamp[1] != 0.0) { h = glm::clamp(h, fn.clamp[0], fn.clamp[1]); } } nextOp = op; } else if (fn.func == TerrainStage::SQUARED) { nextMod = modifier; // Apply parent before clamping if (modifier) { *modifier = (*modifier) * (*modifier); // Optional clamp if both fields are not 0.0 if (fn.clamp[0] != 0.0 || fn.clamp[1] != 0.0) { h = glm::clamp(h, fn.clamp[0], fn.clamp[1]); } } nextOp = op; } else if (fn.func == TerrainStage::CUBED) { nextMod = modifier; // Apply parent before clamping if (modifier) { *modifier = (*modifier) * (*modifier) * (*modifier); // Optional clamp if both fields are not 0.0 if (fn.clamp[0] != 0.0 || fn.clamp[1] != 0.0) { h = glm::clamp(h, fn.clamp[0], fn.clamp[1]); } } nextOp = op; } else { // It's a noise function nextMod = &h; f64v2 ff; f64 tmp; f64 total = 0.0; f64 maxAmplitude = 0.0; f64 amplitude = 1.0; f64 frequency = fn.frequency; for (int i = 0; i < fn.octaves; i++) { // TODO(Ben): Could cut branching switch (fn.func) { case TerrainStage::CUBED_NOISE: case TerrainStage::SQUARED_NOISE: case TerrainStage::NOISE: total += Noise::raw(pos.x * frequency, pos.y * frequency, pos.z * frequency) * amplitude; break; case TerrainStage::RIDGED_NOISE: total += ((1.0 - glm::abs(Noise::raw(pos.x * frequency, pos.y * frequency, pos.z * frequency))) * 2.0 - 1.0) * amplitude; break; case TerrainStage::ABS_NOISE: total += glm::abs(Noise::raw(pos.x * frequency, pos.y * frequency, pos.z * frequency)) * amplitude; break; case TerrainStage::CELLULAR_NOISE: ff = Noise::cellular(pos * (f64)frequency); total += (ff.y - ff.x) * amplitude; break; case TerrainStage::CELLULAR_SQUARED_NOISE: ff = Noise::cellular(pos * (f64)frequency); tmp = ff.y - ff.x; total += tmp * tmp * amplitude; break; case TerrainStage::CELLULAR_CUBED_NOISE: ff = Noise::cellular(pos * (f64)frequency); tmp = ff.y - ff.x; total += tmp * tmp * tmp * amplitude; break; default: break; } frequency *= 2.0; maxAmplitude += amplitude; amplitude *= fn.persistence; } total = (total / maxAmplitude); // Handle any post processes per noise switch (fn.func) { case TerrainStage::CUBED_NOISE: total = total * total * total; break; case TerrainStage::SQUARED_NOISE: total = total * total; break; default: break; } // Conditional scaling. if (fn.low != -1.0 || fn.high != 1.0) { h = total * (fn.high - fn.low) * 0.5 + (fn.high + fn.low) * 0.5; } else { h = total; } // Optional clamp if both fields are not 0.0 if (fn.clamp[0] != 0.0 || fn.clamp[1] != 0.0) { h = glm::clamp(h, (f64)fn.clamp[0], (f64)fn.clamp[1]); } // Apply modifier from parent if needed if (modifier) { h = doOperation(op, h, *modifier); } nextOp = fn.op; } if (fn.children.size()) { // Early exit for speed if (!(nextOp == TerrainOp::MUL && *nextMod == 0.0)) { getNoiseValue(pos, fn.children, nextMod, nextOp, height); } } else { height = doOperation(fn.op, height, h); } } } ================================================ FILE: SoA/SphericalHeightmapGenerator.h ================================================ /// /// SphericalTerrainCpuGenerator.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Generates spherical terrain data and meshes for a planet using the CPU. /// Each planet should own one. /// #pragma once #ifndef SphericalTerrainCpuGenerator_h__ #define SphericalTerrainCpuGenerator_h__ #include "TerrainPatch.h" #include "TerrainPatchMesher.h" #include "VoxelCoordinateSpaces.h" #include "PlanetGenData.h" #include struct NoiseBase; struct PlanetHeightData; // TODO(Ben): Implement this typedef Delegate heightmapGenFunction; class SphericalHeightmapGenerator { public: void init(const PlanetGenData* planetGenData); /// Gets the height at a specific face position. void generateHeightData(OUT PlanetHeightData& height, const VoxelPosition2D& facePosition) const; void generateHeightData(OUT PlanetHeightData& height, const f64v3& normal) const; // Gets the tree id that should be at a specific worldspace position FloraID getTreeID(const Biome* biome, const VoxelPosition2D& facePosition, const f64v3& worldPos) const; // Gets the flora id that should be at a specific worldspace position FloraID getFloraID(const Biome* biome, const VoxelPosition2D& facePosition, const f64v3& worldPos) const; const PlanetGenData* getGenData() const { return m_genData; } private: void generateHeightData(OUT PlanetHeightData& height, const f64v3& pos, const f64v3& normal) const; void recurseChildBiomes(const Biome* biome, const f64v3& pos, f32& height, f64& biggestWeight, const Biome*& bestBiome, f64 baseWeight) const; /// Gets noise value using terrainFuncs /// @return the noise value void getNoiseValue(const f64v3& pos, const Array& funcs, f64* modifier, const TerrainOp& op, f64& height) const; f64 getBaseHeightValue(const f64v3& pos) const; f64 getTemperatureValue(const f64v3& pos, const f64v3& normal, f64 height) const; f64 getHumidityValue(const f64v3& pos, const f64v3& normal, f64 height) const; /// Calculates temperature based on angle with equator /// @param range: The range to scale between /// @angle: Angle from equator /// @param baseTemp: Base temperature static f64 calculateTemperature(f64 range, f64 angle, f64 baseTemp); /// Calculates humidity based on angle with equator /// @param range: The range to scale between /// @angle: Angle from equator /// @param baseTemp: Base humidity static f64 calculateHumidity(f64 range, f64 angle, f64 baseHum); // Computes angle from normalized position static f64 computeAngleFromNormal(const f64v3& normal); const PlanetGenData* m_genData = nullptr; ///< Planet generation data for this generator }; #endif // SphericalTerrainCpuGenerator_h__ ================================================ FILE: SoA/SphericalTerrainComponentRenderer.cpp ================================================ #include "stdafx.h" #include "SphericalTerrainComponentRenderer.h" #include "Camera.h" #include "SpaceSystemComponents.h" #include "TerrainPatchMeshManager.h" #include "TerrainPatch.h" #include "soaUtils.h" #include "ShaderLoader.h" #include #include #include #include SphericalTerrainComponentRenderer::~SphericalTerrainComponentRenderer() { dispose(); } void SphericalTerrainComponentRenderer::initGL() { m_terrainProgram = ShaderLoader::createProgramFromFile("Shaders/SphericalTerrain/SphericalTerrain.vert", "Shaders/SphericalTerrain/SphericalTerrain.frag"); // Set constant uniforms m_terrainProgram.use(); glUniform1i(m_terrainProgram.getUniform("unColorMap"), 1); glUniform1i(m_terrainProgram.getUniform("unGrassTexture"), 2); glUniform1i(m_terrainProgram.getUniform("unRockTexture"), 3); m_terrainProgram.unuse(); m_waterProgram = ShaderLoader::createProgramFromFile("Shaders/SphericalTerrain/SphericalWater.vert", "Shaders/SphericalTerrain/SphericalWater.frag"); // Set constant uniforms m_waterProgram.use(); glUniform1i(m_waterProgram.getUniform("unNormalMap"), 0); glUniform1i(m_waterProgram.getUniform("unColorMap"), 1); m_waterProgram.unuse(); } // TODO: Is this implementation complete? void SphericalTerrainComponentRenderer::draw(SphericalTerrainComponent& cmp, const Camera* camera, const f32v3& lightDir, const f64v3& position, const f32 zCoef, const SpaceLightComponent* spComponent VORB_UNUSED, const AxisRotationComponent* arComponent, const AtmosphereComponent* aComponent) { if (cmp.patches) { f64v3 relativeCameraPos = camera->getPosition() - position; // Sort meshes cmp.meshManager->sortSpericalMeshes(relativeCameraPos); // Draw spherical patches if (cmp.alpha >= 1.0f) { cmp.meshManager->drawSphericalMeshes(relativeCameraPos, camera, arComponent->currentOrientation, m_terrainProgram, m_waterProgram, lightDir, 1.0f, zCoef, aComponent, true); } } } void SphericalTerrainComponentRenderer::dispose() { if (m_terrainProgram.isCreated()) m_terrainProgram.dispose(); if (m_waterProgram.isCreated()) m_waterProgram.dispose(); } ================================================ FILE: SoA/SphericalTerrainComponentRenderer.h ================================================ /// /// SphericalTerrainComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renders spherical terrain components /// #pragma once #ifndef SphericalTerrainComponentRenderer_h__ #define SphericalTerrainComponentRenderer_h__ #include #include class Camera; struct AtmosphereComponent; struct AxisRotationComponent; struct NamePositionComponent; struct SpaceLightComponent; struct SphericalTerrainComponent; class SphericalTerrainComponentRenderer { public: ~SphericalTerrainComponentRenderer(); void initGL(); void draw(SphericalTerrainComponent& cmp, const Camera* camera, const f32v3& lightDir, const f64v3& position, const f32 zCoef, const SpaceLightComponent* spComponent, const AxisRotationComponent* arComponent, const AtmosphereComponent* aComponent); void dispose(); private: vg::GLProgram m_terrainProgram; vg::GLProgram m_waterProgram; }; #endif // SphericalTerrainComponentRenderer_h__ ================================================ FILE: SoA/SphericalTerrainComponentUpdater.cpp ================================================ #include "stdafx.h" #include "SphericalTerrainComponentUpdater.h" #include "SoAState.h" #include "SpaceSystem.h" #include "SpaceSystemAssemblages.h" #include "SpaceSystemComponents.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatchMeshManager.h" #include "VoxelCoordinateSpaces.h" #include "PlanetGenLoader.h" #include "soaUtils.h" void SphericalTerrainComponentUpdater::update(SoaState* state, const f64v3& cameraPos) { SpaceSystem* spaceSystem = state->spaceSystem; for (auto& it : spaceSystem->sphericalTerrain) { SphericalTerrainComponent& stCmp = it.second; const NamePositionComponent& npComponent = spaceSystem->namePosition.get(stCmp.namePositionComponent); const AxisRotationComponent& arComponent = spaceSystem->axisRotation.get(stCmp.axisRotationComponent); /// Calculate camera distance f64v3 relativeCameraPos = arComponent.invCurrentOrientation * (cameraPos - npComponent.position); stCmp.distance = glm::length(relativeCameraPos); // Lazy random planet loading if (stCmp.distance <= LOAD_DIST && !stCmp.planetGenData) { PlanetGenLoader loader; loader.init(state->systemIoManager); PlanetGenData* data = loader.getRandomGenData((f32)stCmp.radius); stCmp.meshManager = new TerrainPatchMeshManager(data); stCmp.cpuGenerator = new SphericalHeightmapGenerator; stCmp.cpuGenerator->init(data); // Do this last to prevent race condition with regular update data->radius = stCmp.radius; stCmp.planetGenData = data; stCmp.sphericalTerrainData->generator = stCmp.cpuGenerator; stCmp.sphericalTerrainData->meshManager = stCmp.meshManager; } // Animation for fade if (stCmp.needsVoxelComponent) { stCmp.alpha -= TERRAIN_ALPHA_STEP; if (stCmp.alpha < 0.0f) { stCmp.alpha = 0.0f; // Force it to unload stCmp.distance = LOAD_DIST * 10.0; } } else { stCmp.alpha += TERRAIN_ALPHA_STEP; if (stCmp.alpha > 1.0f) stCmp.alpha = 1.0f; } if (stCmp.distance <= LOAD_DIST) { if (stCmp.planetGenData && !stCmp.needsVoxelComponent) { // Allocate if needed if (!stCmp.patches) { initPatches(stCmp); } // Update patches for (int i = 0; i < ST_TOTAL_PATCHES; i++) { stCmp.patches[i].update(relativeCameraPos); } } } else { // Out of range, delete everything if (stCmp.patches) { delete[] stCmp.patches; stCmp.patches = nullptr; } } updateVoxelComponentLogic(state, it.first, stCmp); } } void SphericalTerrainComponentUpdater::glUpdate(const SoaState* soaState) { auto& spaceSystem = soaState->spaceSystem; for (auto& it : spaceSystem->sphericalTerrain) { SphericalTerrainComponent& stCmp = it.second; if (stCmp.meshManager && it.second.alpha > 0.0f) stCmp.meshManager->update(); } } void SphericalTerrainComponentUpdater::initPatches(SphericalTerrainComponent& cmp) { const f64& patchWidth = cmp.sphericalTerrainData->patchWidth; // Allocate top level patches cmp.patches = new TerrainPatch[ST_TOTAL_PATCHES]; int center = ST_PATCH_ROW / 2; f64v2 gridPos; int index = 0; // Init all the top level patches for each of the 6 grids for (int face = 0; face < NUM_FACES; face++) { for (int z = 0; z < ST_PATCH_ROW; z++) { for (int x = 0; x < ST_PATCH_ROW; x++) { auto& p = cmp.patches[index++]; gridPos.x = (x - center) * patchWidth; gridPos.y = (z - center) * patchWidth; p.init(gridPos, static_cast(face), 0, cmp.sphericalTerrainData, patchWidth); } } } } void SphericalTerrainComponentUpdater::updateVoxelComponentLogic(SoaState* state, vecs::EntityID eid, SphericalTerrainComponent& stCmp) { SpaceSystem* spaceSystem = state->spaceSystem; // Handle voxel component if (stCmp.needsVoxelComponent) { // Check for creating a new component if (!stCmp.sphericalVoxelComponent) { // TODO(Ben): FarTerrain should be clientSide only // Add far terrain component (CLIENT SIDE) stCmp.farTerrainComponent = SpaceSystemAssemblages::addFarTerrainComponent(spaceSystem, eid, stCmp, stCmp.startVoxelPosition.face); // Add spherical voxel component (SERVER SIDE) stCmp.sphericalVoxelComponent = SpaceSystemAssemblages::addSphericalVoxelComponent(spaceSystem, eid, spaceSystem->sphericalTerrain.getComponentID(eid), stCmp.farTerrainComponent, stCmp.axisRotationComponent, stCmp.namePositionComponent, stCmp.startVoxelPosition.face, state); } // Far terrain face transition if (stCmp.transitionFace != FACE_NONE) { static const f32 FACE_TRANS_DEC = 0.02f; if (stCmp.faceTransTime == START_FACE_TRANS) stCmp.needsFaceTransitionAnimation = true; stCmp.faceTransTime -= FACE_TRANS_DEC; if (stCmp.faceTransTime <= 0.0f) { // TODO(Ben): maybe tell voxels to switch to new face rather than just deleting //spaceSystem->m_sphericalVoxelCT.get(stCmp.sphericalVoxelComponent).transitionFace = stCmp.transitionFace; SpaceSystemAssemblages::removeSphericalVoxelComponent(spaceSystem, eid); // Add spherical voxel component (SERVER SIDE) stCmp.sphericalVoxelComponent = SpaceSystemAssemblages::addSphericalVoxelComponent(spaceSystem, eid, spaceSystem->sphericalTerrain.getComponentID(eid), stCmp.farTerrainComponent, stCmp.axisRotationComponent, stCmp.namePositionComponent, stCmp.transitionFace, state); // Reload the terrain auto& ftCmp = spaceSystem->farTerrain.get(stCmp.farTerrainComponent); ftCmp.transitionFace = stCmp.transitionFace; stCmp.transitionFace = FACE_NONE; stCmp.faceTransTime = 0.0f; std::cout << " RE-INIT Voxels\n"; } } else if (!stCmp.needsFaceTransitionAnimation) { stCmp.faceTransTime = 1.0f; } } else { // Check for deleting components // TODO(Ben): We need to do refcounting for MP! if (stCmp.sphericalVoxelComponent) { // Mark far terrain for fadeout auto& ftCmp = spaceSystem->farTerrain.get(stCmp.farTerrainComponent); if (!ftCmp.shouldFade) { ftCmp.shouldFade = true; ftCmp.alpha = TERRAIN_DEC_START_ALPHA; } else if (ftCmp.alpha < 0.0f) { // We are faded out, so deallocate SpaceSystemAssemblages::removeSphericalVoxelComponent(spaceSystem, eid); SpaceSystemAssemblages::removeFarTerrainComponent(spaceSystem, eid); stCmp.sphericalVoxelComponent = 0; stCmp.farTerrainComponent = 0; } } } } ================================================ FILE: SoA/SphericalTerrainComponentUpdater.h ================================================ /// /// SphericalTerrainComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates SphericalTerrainComponents. /// #pragma once #ifndef SphericalTerrainComponentUpdater_h__ #define SphericalTerrainComponentUpdater_h__ class SpaceSystem; struct SoaState; struct SphericalTerrainComponent; #include "TerrainPatch.h" #include "VoxelCoordinateSpaces.h" #include #include #define LOAD_DIST 800000.0 // Should be even #define ST_PATCH_ROW 2 #define NUM_FACES 6 const int ST_PATCHES_PER_FACE = (ST_PATCH_ROW * ST_PATCH_ROW); const int ST_TOTAL_PATCHES = ST_PATCHES_PER_FACE * NUM_FACES; class SphericalTerrainComponentUpdater { public: void update(SoaState* state, const f64v3& cameraPos); /// Updates openGL specific stuff. Call on render thread void glUpdate(const SoaState* soaState); private: void initPatches(SphericalTerrainComponent& cmp); void updateVoxelComponentLogic(SoaState* state, vecs::EntityID eid, SphericalTerrainComponent& stCmp); }; #endif // SphericalTerrainComponentUpdater_h__ ================================================ FILE: SoA/SphericalVoxelComponentUpdater.cpp ================================================ #include "stdafx.h" #include "SphericalVoxelComponentUpdater.h" #include // For SDL_GetTicks #include "Chunk.h" #include "ChunkAllocator.h" #include "ChunkGrid.h" #include "ChunkIOManager.h" #include "ChunkMeshManager.h" #include "ChunkMeshTask.h" #include "ChunkRenderer.h" #include "ChunkUpdater.h" #include "GameSystem.h" #include "GenerateTask.h" #include "PlanetGenData.h" #include "SoaOptions.h" #include "SoAState.h" #include "SpaceSystem.h" #include "SpaceSystemComponents.h" #include "VoxelSpaceConversions.h" #include "soaUtils.h" #include void SphericalVoxelComponentUpdater::update(const SoaState* soaState) { SpaceSystem* spaceSystem = soaState->spaceSystem; // GameSystem* gameSystem = soaState->gameSystem; if (spaceSystem->sphericalVoxel.getComponentListSize() > 1) { for (auto& it : spaceSystem->sphericalVoxel) { if (it.second.chunkGrids) { updateComponent(it.second); } } } } void SphericalVoxelComponentUpdater::updateComponent(SphericalVoxelComponent& cmp) { m_cmp = &cmp; // Update each world cube face for (int i = 0; i < 6; i++) { updateChunks(cmp.chunkGrids[i], true); cmp.chunkGrids[i].update(); } } // TODO: Implement and remove VORB_UNUSED tags. void SphericalVoxelComponentUpdater::updateChunks(ChunkGrid& grid VORB_UNUSED, bool doGen VORB_UNUSED) { // Get render distance squared f32 renderDist2 = (soaOptions.get(OPT_VOXEL_RENDER_DISTANCE).value.f + (f32)CHUNK_WIDTH); renderDist2 *= renderDist2; } ================================================ FILE: SoA/SphericalVoxelComponentUpdater.h ================================================ /// /// SphericalVoxelComponentUpdater.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 8 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Updates spherical voxel components /// #pragma once #ifndef SphericalVoxelComponentUpdater_h__ #define SphericalVoxelComponentUpdater_h__ #include "ChunkHandle.h" class Camera; class Chunk; class FloraTask; class Frustum; class GameSystem; class GenerateTask; class GeneratedTreeNodes; class ChunkMeshTask; class ChunkGrid; struct SoaState; class SpaceSystem; struct AxisRotationComponent; struct NamePositionComponent; struct SphericalVoxelComponent; struct VoxelPosition3D; #include "VoxelCoordinateSpaces.h" class SphericalVoxelComponentUpdater { public: void update(const SoaState* soaState); private: void updateComponent(SphericalVoxelComponent& cmp); void updateChunks(ChunkGrid& grid, bool doGen); SphericalVoxelComponent* m_cmp = nullptr; ///< Component we are updating }; #endif // SphericalVoxelComponentUpdater_h__ ================================================ FILE: SoA/SsaoRenderStage.cpp ================================================ #include "stdafx.h" #include "SsaoRenderStage.h" #include #include #include #include #include "Errors.h" #include "Camera.h" #include "ShaderLoader.h" void SSAORenderStage::hook(vg::FullQuadVBO* quad, unsigned int width, unsigned int height) { std::mt19937 randGenerator; std::uniform_real_distribution range1(-1.0f, 1.0f); std::uniform_real_distribution range2(0.0f, 1.0f); m_quad = quad; m_texNoise.width = SSAO_NOISE_TEXTURE_SIZE; m_texNoise.height = SSAO_NOISE_TEXTURE_SIZE; // Generate random data i32 pixCount = m_texNoise.width * m_texNoise.height; f32v2* data = new f32v2[pixCount]; Random r(clock()); for (i32 i = 0; i < pixCount; i++) { // TODO(Ben): vec3? data[i].x = range1(randGenerator); data[i].y = range1(randGenerator); data[i] = glm::normalize(data[i]); } // Build noise texture glGenTextures(1, &m_texNoise.id); glBindTexture(GL_TEXTURE_2D, m_texNoise.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, m_texNoise.width, m_texNoise.height, 0, GL_RG, GL_FLOAT, data); vg::SamplerState::POINT_WRAP.set(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); delete[] data; m_ssaoTarget.setSize(width, height); m_ssaoTarget.init(vg::TextureInternalFormat::R32F); m_sampleKernel.resize(SSAO_SAMPLE_KERNEL_SIZE); for (unsigned int i = 0; i < SSAO_SAMPLE_KERNEL_SIZE; i++) { m_sampleKernel[i] = glm::normalize(f32v3(range1(randGenerator), range1(randGenerator), range2(randGenerator))); // Use accelerating interpolation f32 scale = (f32)i / (f32)SSAO_SAMPLE_KERNEL_SIZE; scale = lerp(0.1f, 1.0f, scale * scale); m_sampleKernel[i] *= scale; } } void SSAORenderStage::dispose(StaticLoadContext& context VORB_MAYBE_UNUSED) { if (m_texNoise.id) { glDeleteTextures(1, &m_texNoise.id); m_texNoise.id = 0; } m_ssaoShader.dispose(); m_ssaoTarget.dispose(); m_blurShader.dispose(); } void SSAORenderStage::render(const Camera* camera) { glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); const f32m4& projectionMatrix = camera->getProjectionMatrix(); const f32m4& viewMatrix = camera->getViewMatrix(); { // SSAO pass m_ssaoTarget.use(); glClear(GL_COLOR_BUFFER_BIT); // Bind textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_depthTexture); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_normalTexture); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_texNoise.id); // Lazy shader init // TODO(Ben): Make it not lazy if (!m_ssaoShader.isCreated()) { m_ssaoShader = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/SSAO.frag"); m_ssaoShader.use(); glUniform1i(m_ssaoShader.getUniform("unTexDepth"), 0); glUniform1i(m_ssaoShader.getUniform("unTexNormal"), 1); glUniform1i(m_ssaoShader.getUniform("unTexNoise"), 2); glUniform3fv(glGetUniformLocation(m_ssaoShader.getID(), "unSampleKernel"), m_sampleKernel.size(), &m_sampleKernel.data()->x); glUniform2f(m_ssaoShader.getUniform("unNoiseScale"), (f32)m_ssaoTarget.getWidth() / SSAO_NOISE_TEXTURE_SIZE, (f32)m_ssaoTarget.getHeight() / SSAO_NOISE_TEXTURE_SIZE); } else { m_ssaoShader.use(); } glUniformMatrix4fv(m_ssaoShader.getUniform("unViewMatrix"), 1, false, &viewMatrix[0][0]); glUniformMatrix4fv(m_ssaoShader.getUniform("unProjectionMatrix"), 1, false, &projectionMatrix[0][0]); glUniformMatrix4fv(m_ssaoShader.getUniform("unInvProjectionMatrix"), 1, false, &glm::inverse(projectionMatrix)[0][0]); m_quad->draw(); } { // Blur pass // Bind hdr framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_hdrFrameBuffer); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_colorTexture); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_ssaoTarget.getTextureID()); // Lazy shader init // TODO(Ben): Make it not lazy if (!m_blurShader.isCreated()) { m_blurShader = ShaderLoader::createProgramFromFile("Shaders/PostProcessing/PassThrough.vert", "Shaders/PostProcessing/SSAOBlur.frag"); m_blurShader.use(); glUniform1i(m_blurShader.getUniform("unTexColor"), 0); glUniform1i(m_blurShader.getUniform("unTexSSAO"), 1); glUniform1f(m_blurShader.getUniform("unBlurAmount"), (float)SSAO_BLUR_AMOUNT); } else { m_blurShader.use(); } m_quad->draw(); vg::GLProgram::unuse(); } glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); } void SSAORenderStage::reloadShaders() { m_ssaoShader.dispose(); m_blurShader.dispose(); } ================================================ FILE: SoA/SsaoRenderStage.h ================================================ #pragma once #include "IRenderStage.h" #define SSAO_NOISE_TEXTURE_SIZE 4 #define SSAO_SAMPLE_KERNEL_SIZE 32 #define SSAO_BLUR_AMOUNT 2 #include #include #include #include #include #include class SSAORenderStage : public IRenderStage { public: void hook(vg::FullQuadVBO* quad, unsigned int width, unsigned int height); virtual void dispose(StaticLoadContext& context) override; /// Draws the render stage virtual void render(const Camera* camera = nullptr) override; inline void set(VGTexture depthTexture, VGTexture normalTexture, VGTexture colorTexture, VGFramebuffer hdrFrameBuffer) { m_depthTexture = depthTexture; m_normalTexture = normalTexture; m_colorTexture = colorTexture; m_hdrFrameBuffer = hdrFrameBuffer; } void reloadShaders(); private: vg::GLProgram m_ssaoShader; ///< SSAO effect vg::GLRenderTarget m_ssaoTarget; ///< SSAO render target vg::GLProgram m_blurShader; ///< Blurring to reduce noise vg::FullQuadVBO* m_quad; ///< For use in processing through data vg::Texture m_texNoise; ///< A noise texture to make low sample amounts less obvious VGTexture m_depthTexture; VGTexture m_normalTexture; VGTexture m_colorTexture; VGFramebuffer m_hdrFrameBuffer; std::vector m_sampleKernel; }; ================================================ FILE: SoA/StarComponentRenderer.cpp ================================================ #include "stdafx.h" #include "StarComponentRenderer.h" #include "Errors.h" #include "ModPathResolver.h" #include "RenderUtils.h" #include "ShaderLoader.h" #include "SpaceSystemComponents.h" #include "soaUtils.h" #include #include #include #include #include #include #include #define MIN_TMP 800.0 #define TMP_RANGE 29200.0 #define ICOSPHERE_SUBDIVISIONS 5 namespace { const cString OCCLUSION_VERT_SRC = R"( uniform vec3 unCenterScreenspace; uniform float unSize; in vec2 vPosition; void main() { gl_Position.xyz = unCenterScreenspace; gl_Position.xy += vPosition * unSize; gl_Position.w = 1.0; } )"; const cString OCCLUSION_FRAG_SRC = R"( out vec4 pColor; void main() { pColor = vec4(0.0, 0.0, 0.0, 0.0); } )"; } StarComponentRenderer::StarComponentRenderer() { // Empty } StarComponentRenderer::~StarComponentRenderer() { dispose(); } void StarComponentRenderer::init(const ModPathResolver* textureResolver) { m_textureResolver = textureResolver; m_tempColorMap.width = std::numeric_limits::max(); } void StarComponentRenderer::initGL() { if (!m_starProgram.isCreated()) buildShaders(); if (!m_sVbo) buildMesh(); if (m_tempColorMap.width == std::numeric_limits::max()) loadTempColorMap(); } void StarComponentRenderer::drawStar(const StarComponent& sCmp, const f32m4& VP, const f64q& orientation, const f32v3& relCamPos, const f32 zCoef) { if (sCmp.visibility == 0.0f) return; m_starProgram.use(); // Calculate color f32v3 tColor = calculateStarColor(sCmp) + getTempColorShift(sCmp); // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)orientation.x; orientationF32.y = (f32)orientation.y; orientationF32.z = (f32)orientation.z; orientationF32.w = (f32)orientation.w; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); // Set up matrix f32m4 WVP(1.0); setMatrixTranslation(WVP, -relCamPos); WVP = VP * WVP * glm::scale(f32v3(sCmp.radius)) * rotationMatrix; // Upload uniforms // Upload uniforms static f64 dt = 0.0; dt += 0.0001; glUniform1f(unDT, (f32)dt); glUniformMatrix4fv(unWVP, 1, GL_FALSE, &WVP[0][0]); glUniform3fv(m_starProgram.getUniform("unColor"), 1, &tColor[0]); glUniform1f(m_starProgram.getUniform("unRadius"), (f32)sCmp.radius); f32v3 unCenterDir = glm::normalize(relCamPos); glUniform3fv(m_starProgram.getUniform("unCenterDir"), 1, &unCenterDir[0]); // For logarithmic Z buffer glUniform1f(m_starProgram.getUniform("unZCoef"), zCoef); // Bind VAO glBindVertexArray(m_sVao); glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, 0); glBindVertexArray(0); m_starProgram.unuse(); } void StarComponentRenderer::drawCorona(StarComponent& sCmp, const f32m4& VP, const f32m4& V, const f32v3& relCamPos, const f32 zCoef) { f32v3 center(-relCamPos); f32v3 camRight(V[0][0], V[1][0], V[2][0]); f32v3 camUp(V[0][1], V[1][1], V[2][1]); if (sCmp.visibility == 0.0f) return; m_coronaProgram.use(); // Corona color f32v3 tColor = getTempColorShift(sCmp); // Upload uniforms glUniform3fv(m_coronaProgram.getUniform("unCameraRight"), 1, &camRight[0]); glUniform3fv(m_coronaProgram.getUniform("unCameraUp"), 1, &camUp[0]); glUniform3fv(m_coronaProgram.getUniform("unCenter"), 1, ¢er[0]); glUniform3fv(m_coronaProgram.getUniform("unColor"), 1, &tColor[0]); // For logarithmic Z buffer glUniform1f(m_coronaProgram.getUniform("unZCoef"), zCoef); // Size glUniform1f(m_coronaProgram.getUniform("unMaxSize"), 4.0f); glUniform1f(m_coronaProgram.getUniform("unStarRadius"), (f32)sCmp.radius); // Time static f64 dt = 0.0; dt += 0.0001; glUniform1f(m_coronaProgram.getUniform("unDT"), (f32)dt); glUniformMatrix4fv(m_coronaProgram.getUniform("unWVP"), 1, GL_FALSE, &VP[0][0]); // Bind VAO glBindVertexArray(m_cVao); glDepthMask(GL_FALSE); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glDepthMask(GL_TRUE); glBindVertexArray(0); m_coronaProgram.unuse(); } f32v3 hdrs(f32v3 v) { return f32v3(1.0f) - glm::exp(v * -3.0f); } void StarComponentRenderer::drawGlow(const StarComponent& sCmp, const f32m4& VP, const f64v3& relCamPos, float aspectRatio, const f32v3& viewDirW, const f32v3& viewRightW, const f32v3& colorMult /* = f32v3(1.0f) */ ) { if (sCmp.visibility == 0.0f) return; // Compute desired size based on distance and ratio of mass to Sol mass f64 s = calculateGlowSize(sCmp, relCamPos) * sCmp.visibility; // Don't render if its too small if (s <= 0.0) return; f32v2 dims(s, s * aspectRatio); m_glowProgram.use(); f32 scale = glm::clamp((f32)((sCmp.temperature - MIN_TMP) / TMP_RANGE), 0.0f, 1.0f); // Upload uniforms f32v3 center(-relCamPos); glUniform3fv(m_glowProgram.getUniform("unCenter"), 1, ¢er[0]); glUniform3fv(m_glowProgram.getUniform("unColorMult"), 1, &colorMult[0]); glUniform1f(m_glowProgram.getUniform("unColorMapU"), scale); glUniform2fv(m_glowProgram.getUniform("unDims"), 1, &dims[0]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_glowColorMap); // For sparkles f32v3 vs = viewDirW - viewRightW; glUniform1f(m_glowProgram.getUniform("unNoiseZ"), (vs.x + vs.y - vs.z) * 0.125f); glUniformMatrix4fv(m_glowProgram.getUniform("unVP"), 1, GL_FALSE, &VP[0][0]); // Bind VAO glBindVertexArray(m_gVao); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); glBindVertexArray(0); m_glowProgram.unuse(); } void StarComponentRenderer::updateOcclusionQuery(StarComponent& sCmp, const f32 zCoef, const f32m4& VP, const f64v3& relCamPos) { if (!m_occlusionProgram.isCreated()) { m_occlusionProgram = vg::ShaderManager::createProgram(OCCLUSION_VERT_SRC, OCCLUSION_FRAG_SRC); glGenVertexArrays(1, &m_oVao); glBindVertexArray(m_oVao); vg::GpuMemory::bindBuffer(m_cVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::bindBuffer(m_cIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); m_occlusionProgram.enableVertexAttribArrays(); glVertexAttribPointer(m_occlusionProgram.getAttribute("vPosition"), 2, GL_FLOAT, GL_FALSE, 0, 0); glBindVertexArray(0); } if (sCmp.occlusionQuery[0] == 0) { glGenQueries(2, sCmp.occlusionQuery); } else { int totalSamples = 0; int passedSamples = 0; glGetQueryObjectiv(sCmp.occlusionQuery[0], GL_QUERY_RESULT, &totalSamples); glGetQueryObjectiv(sCmp.occlusionQuery[1], GL_QUERY_RESULT, &passedSamples); if (passedSamples == 0) { sCmp.visibility = 0.0f; } else { sCmp.visibility = (f32)passedSamples / (f32)totalSamples; } } // Have to calculate on the CPU since we need 64 bit precision. Otherwise // we get the "phantom star" bug. f64v4 pos(-relCamPos, 1.0); f64v4 gl_Position = f64m4(VP) * pos; f64v3 centerScreenspace64(gl_Position.x / gl_Position.w, gl_Position.y / gl_Position.w, gl_Position.z / gl_Position.w); if (gl_Position.z < 0.0) { centerScreenspace64.x = -100.0f; // force it off screen } else { centerScreenspace64.z = log2(glm::max(1e-6, gl_Position.w + 1.0)) * zCoef - 1.0; centerScreenspace64.z *= gl_Position.w; } f32v3 centerScreenspace(centerScreenspace64); f64 s = calculateGlowSize(sCmp, relCamPos) / 128.0; s = glm::max(0.005, s); // make sure it never gets too small m_occlusionProgram.use(); // Upload uniforms glUniform3fv(m_occlusionProgram.getUniform("unCenterScreenspace"), 1, ¢erScreenspace[0]); glUniform1f(m_occlusionProgram.getUniform("unSize"), (f32)s); glBindVertexArray(m_oVao); glDepthMask(GL_FALSE); glBeginQuery(GL_SAMPLES_PASSED, sCmp.occlusionQuery[0]); glDisable(GL_DEPTH_TEST); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEndQuery(GL_SAMPLES_PASSED); glBeginQuery(GL_SAMPLES_PASSED, sCmp.occlusionQuery[1]); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0); glEndQuery(GL_SAMPLES_PASSED); glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); glBindVertexArray(0); m_occlusionProgram.unuse(); } void StarComponentRenderer::dispose() { disposeShaders(); disposeBuffers(); if (m_tempColorMap.data) { vg::ImageIO().free(m_tempColorMap); m_tempColorMap.data = nullptr; m_tempColorMap.width = -1; } } void StarComponentRenderer::disposeShaders() { if (m_starProgram.isCreated()) m_starProgram.dispose(); if (m_coronaProgram.isCreated()) m_coronaProgram.dispose(); if (m_glowProgram.isCreated()) m_glowProgram.dispose(); if (m_occlusionProgram.isCreated()) { m_occlusionProgram.dispose(); // Also destroy VAO since they are related glDeleteVertexArrays(1, &m_oVao); m_oVao = 0; } disposeBuffers(); } void StarComponentRenderer::disposeBuffers() { // Dispose buffers too for proper reload if (m_sVbo) { vg::GpuMemory::freeBuffer(m_sVbo); } if (m_sIbo) { vg::GpuMemory::freeBuffer(m_sIbo); } if (m_sVao) { glDeleteVertexArrays(1, &m_sVao); m_sVao = 0; } if (m_cVbo) { vg::GpuMemory::freeBuffer(m_cVbo); } if (m_cIbo) { vg::GpuMemory::freeBuffer(m_cIbo); } if (m_cVao) { glDeleteVertexArrays(1, &m_cVao); m_cVao = 0; } if (m_gVao) { glDeleteVertexArrays(1, &m_gVao); m_gVao = 0; } } void StarComponentRenderer::buildShaders() { m_starProgram = ShaderLoader::createProgramFromFile("Shaders/Star/star.vert", "Shaders/Star/star.frag"); m_starProgram.use(); unWVP = m_starProgram.getUniform("unWVP"); unDT = m_starProgram.getUniform("unDT"); m_starProgram.unuse(); m_coronaProgram = ShaderLoader::createProgramFromFile("Shaders/Star/corona.vert", "Shaders/Star/corona.frag"); m_glowProgram = ShaderLoader::createProgramFromFile("Shaders/Star/glow.vert", "Shaders/Star/glow.frag"); m_glowProgram.use(); glUniform1i(m_glowProgram.getUniform("unColorMap"), 0); m_glowProgram.unuse(); } void StarComponentRenderer::buildMesh() { // Build star mesh std::vector indices; std::vector positions; // TODO(Ben): Optimize with LOD for far viewing vmesh::generateIcosphereMesh(ICOSPHERE_SUBDIVISIONS, indices, positions); m_numIndices = indices.size(); glGenVertexArrays(1, &m_sVao); glBindVertexArray(m_sVao); vg::GpuMemory::createBuffer(m_sVbo); vg::GpuMemory::createBuffer(m_sIbo); vg::GpuMemory::bindBuffer(m_sVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_sVbo, vg::BufferTarget::ARRAY_BUFFER, positions.size() * sizeof(f32v3), positions.data(), vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(m_sIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_sIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(ui32), indices.data(), vg::BufferUsageHint::STATIC_DRAW); m_starProgram.enableVertexAttribArrays(); glVertexAttribPointer(m_starProgram.getAttribute("vPosition"), 3, GL_FLOAT, GL_FALSE, 0, 0); // Build corona and glow mesh glGenVertexArrays(1, &m_cVao); glBindVertexArray(m_cVao); vg::GpuMemory::createBuffer(m_cVbo); vg::GpuMemory::createBuffer(m_cIbo); f32v2 cPositions[4] = { f32v2(-1.0f, 1.0f), f32v2(-1.0f, -1.0f), f32v2(1.0f, -1.0f), f32v2(1.0f, 1.0f) }; ui16 cIndices[6] = { 0, 1, 2, 2, 3, 0 }; vg::GpuMemory::bindBuffer(m_cVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_cVbo, vg::BufferTarget::ARRAY_BUFFER, sizeof(cPositions), cPositions, vg::BufferUsageHint::STATIC_DRAW); vg::GpuMemory::bindBuffer(m_cIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_cIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, sizeof(cIndices), cIndices, vg::BufferUsageHint::STATIC_DRAW); m_coronaProgram.enableVertexAttribArrays(); glVertexAttribPointer(m_coronaProgram.getAttribute("vPosition"), 2, GL_FLOAT, GL_FALSE, 0, 0); // Build glow VAO glGenVertexArrays(1, &m_gVao); glBindVertexArray(m_gVao); vg::GpuMemory::bindBuffer(m_cVbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::bindBuffer(m_cIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); m_glowProgram.enableVertexAttribArrays(); glVertexAttribPointer(m_glowProgram.getAttribute("vPosition"), 2, GL_FLOAT, GL_FALSE, 0, 0); glBindVertexArray(0); } void StarComponentRenderer::loadTempColorMap() { // Load all the bitmap data vio::Path path; m_textureResolver->resolvePath("Sky/Star/star_spectrum_1.png", path); m_tempColorMap = vg::ImageIO().load(path); if (!m_tempColorMap.data) { fprintf(stderr, "ERROR: Failed to load Sky/Star/star_spectrum_1.png\n"); } m_textureResolver->resolvePath("Sky/Star/star_spectrum_2.png", path); vg::ScopedBitmapResource res2(vg::ImageIO().load(path)); if (!res2.data) { fprintf(stderr, "ERROR: Failed to load Sky/Star/star_spectrum_2.png\n"); } m_textureResolver->resolvePath("Sky/Star/star_spectrum_3.png", path); vg::ScopedBitmapResource res3(vg::ImageIO().load(path)); if (!res3.data) { fprintf(stderr, "ERROR: Failed to load Sky/Star/star_spectrum_3.png\n"); } // Error check dimensions if ((m_tempColorMap.width != res2.width) || (res2.width != res3.width) || (m_tempColorMap.height != res2.height) || (res2.height != res3.height)) { pError("star_spectrum images should all be the same dimensions!"); } // Create the texture array if (m_glowColorMap == 0) glGenTextures(1, &m_glowColorMap); glBindTexture(GL_TEXTURE_2D, m_glowColorMap); // Set up storage glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_tempColorMap.width, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // Upload the data to VRAM glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tempColorMap.width, m_tempColorMap.height, GL_RGBA, GL_UNSIGNED_BYTE, m_tempColorMap.data); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 1, res2.width, res2.height, GL_RGBA, GL_UNSIGNED_BYTE, res2.data); // Copy res3 twice so we get PO2 texture glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 2, res3.width, res3.height, GL_RGBA, GL_UNSIGNED_BYTE, res3.data); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 3, res3.width, res3.height, GL_RGBA, GL_UNSIGNED_BYTE, res3.data); // Set up tex parameters vg::SamplerState::LINEAR_CLAMP.set(GL_TEXTURE_2D); // No mipmapping glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); // Unbind glBindTexture(GL_TEXTURE_2D, 0); // Check if we had any errors checkGlError("StarComponentRenderer::loadTempColorMap()"); } void StarComponentRenderer::loadGlowTextures() { // TODO(Ben): remove this /*vio::Path path; m_textureResolver->resolvePath("Sky/Star/star_glow_overlay.png", path); vg::ScopedBitmapResource rs2 = vg::ImageIO().load(path); if (!m_tempColorMap.data) { fprintf(stderr, "ERROR: Failed to load Sky/Star/star_glow_overlay.png\n"); } else { m_glowTexture = vg::GpuMemory::uploadTexture(&rs2); }*/ } f64 StarComponentRenderer::calculateGlowSize(const StarComponent& sCmp, const f64v3& relCamPos) { static const f64 DSUN = 1392684.0; static const f64 TSUN = 5778.0; // Georg's magic formula f64 d = glm::length(relCamPos); // Distance f64 D = sCmp.radius * 2.0 * DSUN; f64 L = (D * D) * pow(sCmp.temperature / TSUN, 4.0); // Luminosity return 0.016 * pow(L, 0.25) / pow(d, 0.5); // Size } f32v3 StarComponentRenderer::calculateStarColor(const StarComponent& sCmp) { // Calculate temperature color f32v3 tColor; f32 scale = (f32)(m_tempColorMap.width * (sCmp.temperature - MIN_TMP) / TMP_RANGE); scale = glm::clamp(scale, 0.0f, (f32)m_tempColorMap.width); ui32 rScale = (ui32)(scale + 0.5f); ui32 iScale = (ui32)scale; if (rScale >= m_tempColorMap.width) rScale = m_tempColorMap.width - 1; if (iScale < rScale) { // Interpolate down if (iScale == 0) { tColor = getColor(iScale); } else { tColor = lerp(getColor(iScale), getColor(rScale), scale - (f32)iScale); } } else { // Interpolate up if (rScale >= m_tempColorMap.width-1) { tColor = getColor((int)m_tempColorMap.width - 1); } else { tColor = lerp(getColor(rScale), getColor(rScale + 1), scale - (f32)rScale); } } return tColor; } f32v3 StarComponentRenderer::getColor(int index) { const ui8v4& bytes = m_tempColorMap.bytesUI8v4[index]; return f32v3(bytes.r / 255.0f, bytes.g / 255.0f, bytes.b / 255.0f); } f32v3 StarComponentRenderer::getTempColorShift(const StarComponent& sCmp) { return f32v3(sCmp.temperature * (0.0534 / 255.0) - (43.0 / 255.0), sCmp.temperature * (0.0628 / 255.0) - (77.0 / 255.0), sCmp.temperature * (0.0735 / 255.0) - (115.0 / 255.0)); } ================================================ FILE: SoA/StarComponentRenderer.h ================================================ /// /// StarComponentRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 9 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Renderer for StarComponents /// #pragma once #ifndef StarComponentRenderer_h__ #define StarComponentRenderer_h__ #include #include #include #include #include #include class ModPathResolver; struct StarComponent; class StarComponentRenderer { public: StarComponentRenderer(); ~StarComponentRenderer(); void init(const ModPathResolver* textureResolver); void initGL(); void drawStar(const StarComponent& sCmp, const f32m4& VP, const f64q& orientation, const f32v3& relCamPos, const f32 zCoef); void drawCorona(StarComponent& sCmp, const f32m4& VP, const f32m4& V, const f32v3& relCamPos, const f32 zCoef); void drawGlow(const StarComponent& sCmp, const f32m4& VP, const f64v3& relCamPos, float aspectRatio, const f32v3& viewDirW, const f32v3& viewRightW, const f32v3& colorMult = f32v3(1.0f)); void updateOcclusionQuery(StarComponent& sCmp, const f32 zCoef, const f32m4& VP, const f64v3& relCamPos); void dispose(); void disposeShaders(); void disposeBuffers(); f32v3 calculateStarColor(const StarComponent& sCmp); f64 calculateGlowSize(const StarComponent& sCmp, const f64v3& relCamPos); private: void buildShaders(); void buildMesh(); void loadTempColorMap(); void loadGlowTextures(); f32v3 getColor(int index); f32v3 getTempColorShift(const StarComponent& sCmp); vg::GLProgram m_starProgram; vg::GLProgram m_coronaProgram; vg::GLProgram m_glowProgram; vg::GLProgram m_occlusionProgram; // Star VGBuffer m_sVbo = 0; VGIndexBuffer m_sIbo = 0; VGVertexArray m_sVao = 0; // Corona VGBuffer m_cVbo = 0; VGIndexBuffer m_cIbo = 0; VGVertexArray m_cVao = 0; // Glow VGVertexArray m_gVao = 0; // Occlusion VGVertexArray m_oVao = 0; vg::BitmapResource m_tempColorMap; VGTexture m_glowColorMap = 0; int m_numIndices = 0; // TODO(Ben): UBO VGUniform unWVP; VGUniform unDT; const ModPathResolver* m_textureResolver = nullptr; }; #endif // StarComponentRenderer_h__ ================================================ FILE: SoA/Startup.cpp ================================================ #include "stdafx.h" #include "Startup.h" namespace { void printHelp() { printf(R"( Command-line arguments: "-a" to launch main application "-c" to bring up the console "-h" for this help text "-q" to do nothing ... Press any key to exit ... )"); } } Startup startup(int argc, cString* argv) { // Application mode is the default Startup mode = Startup::HELP; bool shouldOutputHelp = false; // Check if another argument exists if (argc > 1) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-a") == 0) { mode = Startup::APP; } else if (strcmp(argv[i], "-c") == 0) { mode = Startup::CONSOLE; } else if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) { shouldOutputHelp = true; break; } else if (strcmp(argv[i], "-q") == 0) { mode = Startup::EXIT; } else { printf("Unrecognized option:\n%s\n", argv[i]); } } } // Print help if they wanted it or didn't enter anything meaningful if (shouldOutputHelp || mode == Startup::HELP) { printHelp(); } return mode; } ================================================ FILE: SoA/Startup.h ================================================ // // Startup.h // Seed of Andromeda // // Created by Cristian Zaloj on 29 Jun 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef Startup_h__ #define Startup_h__ #include "Vorb/types.h" /*! @brief Designates the startup format. */ enum class Startup { APP, CONSOLE, HELP, EXIT }; /*! @brief Determines how to startup the application from its arguments. * * @param argc: Number of process arguments. * @param argv: Values of process arguments. * @return The way to start the application. */ Startup startup(int argc, cString* argv); #endif // Startup_h__ ================================================ FILE: SoA/SystemARRenderer.cpp ================================================ #include "stdafx.h" #include "SystemARRenderer.h" #include "Camera.h" #include "MainMenuSystemViewer.h" #include "ModPathResolver.h" #include "ShaderLoader.h" #include "SpaceSystem.h" #include "soaUtils.h" #include #include #include #include #include #include #include namespace { const cString VERT_SRC = R"( uniform mat4 unWVP; in vec4 vPosition; in float vAngle; out float fAngle; #include "Shaders/Utils/logz.glsl" void main() { fAngle = vAngle; gl_Position = unWVP * vPosition; applyLogZ(); } )"; const cString FRAG_SRC = R"( uniform vec4 unColor; uniform float currentAngle; in float fAngle; out vec4 pColor; void main() { pColor = unColor * vec4(1.0, 1.0, 1.0, 1.0 - mod(fAngle + currentAngle, 1.0)); } )"; } SystemARRenderer::SystemARRenderer() { // Empty } SystemARRenderer::~SystemARRenderer() { dispose(); } void SystemARRenderer::init(const ModPathResolver* textureResolver) { m_textureResolver = textureResolver; } void SystemARRenderer::initGL() { if (!m_colorProgram.isCreated()) m_colorProgram = ShaderLoader::createProgram("SystemAR", VERT_SRC, FRAG_SRC); if (m_selectorTexture == 0) loadTextures(); } void SystemARRenderer::draw(SpaceSystem* spaceSystem, const Camera* camera, OPT const MainMenuSystemViewer* systemViewer, const f32v2& viewport) { // Get handles so we don't have huge parameter lists m_spaceSystem = spaceSystem; m_camera = camera; m_systemViewer = systemViewer; m_viewport = viewport; m_zCoef = computeZCoef(camera->getFarClip()); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glDepthMask(GL_FALSE); glDepthFunc(GL_LEQUAL); drawPaths(); if (m_systemViewer) { drawHUD(); } glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void SystemARRenderer::dispose() { if (m_colorProgram.isCreated()) { m_colorProgram.dispose(); } if (m_spriteBatch) { m_spriteBatch->dispose(); m_spriteFont->dispose(); delete m_spriteBatch; m_spriteBatch = nullptr; delete m_spriteFont; m_spriteFont = nullptr; } } void SystemARRenderer::loadTextures() { { // Selector vio::Path path; m_textureResolver->resolvePath("GUI/selector.png", path); vg::ScopedBitmapResource res(vg::ImageIO().load(path)); if (!res.data) { fprintf(stderr, "ERROR: Failed to load GUI/selector.png\n"); } m_selectorTexture = vg::GpuMemory::uploadTexture(&res, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP_MIPMAP); } { // Barycenter vio::Path path; m_textureResolver->resolvePath("GUI/barycenter.png", path); vg::ScopedBitmapResource res(vg::ImageIO().load(path)); if (!res.data) { fprintf(stderr, "ERROR: Failed to load GUI/barycenter.png\n"); } m_baryTexture = vg::GpuMemory::uploadTexture(&res, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_CLAMP_MIPMAP); } } void SystemARRenderer::drawPaths() { float blendFactor; // Draw paths m_colorProgram.use(); m_colorProgram.enableVertexAttribArrays(); // For logarithmic Z buffer glUniform1f(m_colorProgram.getUniform("unZCoef"), m_zCoef); glLineWidth(3.0f); f32m4 wvp = m_camera->getProjectionMatrix() * m_camera->getViewMatrix(); for (auto& it : m_spaceSystem->orbit) { auto& cmp = it.second; bool isSelected = false; f32v4 oldPathColor; // To cache path color since we force it to a different one if (m_systemViewer) { // Get the augmented reality data const MainMenuSystemViewer::BodyArData* bodyArData = m_systemViewer->finBodyAr(it.first); if (bodyArData == nullptr) continue; // ui8v3 ui8Color; // If its selected we force a different color if (m_systemViewer->getTargetBody() == it.first) { isSelected = true; oldPathColor = cmp.pathColor[0]; cmp.pathColor[0] = m_spaceSystem->pathColorMap["Selected"].second; blendFactor = 0.0; } else { // Hermite interpolated alpha blendFactor = hermite(bodyArData->hoverTime); } } else { blendFactor = 0.0; } if (cmp.parentOrbId) { OrbitComponent& pOrbCmp = m_spaceSystem->orbit.get(cmp.parentOrbId); m_orbitComponentRenderer.drawPath(cmp, m_colorProgram, wvp, &m_spaceSystem->namePosition.getFromEntity(it.first), m_camera->getPosition(), blendFactor, &m_spaceSystem->namePosition.get(pOrbCmp.npID)); } else { m_orbitComponentRenderer.drawPath(cmp, m_colorProgram, wvp, &m_spaceSystem->namePosition.getFromEntity(it.first), m_camera->getPosition(), blendFactor); } // Restore path color if (isSelected) cmp.pathColor[0] = oldPathColor; } m_colorProgram.disableVertexAttribArrays(); m_colorProgram.unuse(); } void SystemARRenderer::drawHUD() { const f32 ROTATION_FACTOR = (f32)(M_PI + M_PI / 4.0); static f32 dt = 0.0; dt += 0.01f; // Lazily load spritebatch if (!m_spriteBatch) { m_spriteBatch = new vg::SpriteBatch(true, true); m_spriteFont = new vg::SpriteFont(); m_spriteFont->init("Fonts/orbitron_bold-webfont.ttf", 32); } m_spriteBatch->begin(); // Render all bodies for (auto& it : m_spaceSystem->orbit) { auto& oCmp = it.second; auto& npCmp = m_spaceSystem->namePosition.get(oCmp.npID); // Get the augmented reality data const MainMenuSystemViewer::BodyArData* bodyArData = m_systemViewer->finBodyAr(it.first); if (bodyArData == nullptr) continue; if (bodyArData->inFrustum) { f64v3 position = npCmp.position; f64v3 relativePos = position - m_camera->getPosition(); f64 distance = glm::length(relativePos); color4 textColor; f32 hoverTime = bodyArData->hoverTime; // Get screen position f32v3 screenCoords = m_camera->worldToScreenPointLogZ(relativePos, (f64)m_camera->getFarClip()); f32v2 xyScreenCoords(screenCoords.x * m_viewport.x, screenCoords.y * m_viewport.y); // Get a smooth interpolator with hermite f32 interpolator = hermite(hoverTime); // Calculate colors ui8v3 ui8Color; // If its selected we use a different color bool isSelected = false; if (m_systemViewer->getTargetBody() == it.first) { isSelected = true; ui8Color = ui8v3(m_spaceSystem->pathColorMap["Selected"].second * 255.0f); } else { ui8Color = ui8v3(lerp(oCmp.pathColor[0], oCmp.pathColor[1], interpolator) * 255.0f); } color4 oColor(ui8Color.r, ui8Color.g, ui8Color.b, 255u); textColor.lerp(color::LightGray, color::White, interpolator); f32 selectorSize = bodyArData->selectorSize; // Only render if it isn't too big if (selectorSize < MainMenuSystemViewer::MAX_SELECTOR_SIZE) { // Alpha interpolation from size so they fade out f32 low = MainMenuSystemViewer::MAX_SELECTOR_SIZE * 0.7f; if (selectorSize > low) { // Fade out when close oColor.a = (ui8)((1.0f - (selectorSize - low) / (MainMenuSystemViewer::MAX_SELECTOR_SIZE - low)) * 255); textColor.a = oColor.a; } else { f64 d = distance - (f64)low; // Fade name based on distance switch (oCmp.type) { case SpaceObjectType::STAR: textColor.a = oColor.a = (ui8)(glm::max(0.0, (f64)textColor.a - d * 0.00000000001)); break; case SpaceObjectType::BARYCENTER: case SpaceObjectType::PLANET: case SpaceObjectType::DWARF_PLANET: textColor.a = oColor.a = (ui8)(glm::max(0.0, (f64)textColor.a - d * 0.000000001)); break; default: textColor.a = oColor.a = (ui8)(glm::max(0.0, (f64)textColor.a - d * 0.000001)); break; } } // Pick texture VGTexture tx; if (oCmp.type == SpaceObjectType::BARYCENTER) { tx = m_baryTexture; selectorSize = MainMenuSystemViewer::MIN_SELECTOR_SIZE * 2.5f - (f32)(distance * 0.00000000001); if (selectorSize < 0.0) continue; interpolator = 0.0f; // Don't rotate barycenters } else { tx = m_selectorTexture; } // Draw Indicator m_spriteBatch->draw(tx, nullptr, nullptr, xyScreenCoords, f32v2(0.5f, 0.5f), f32v2(selectorSize), interpolator * ROTATION_FACTOR, oColor, screenCoords.z); // Text offset and scaling const f32v2 textOffset(selectorSize / 2.0f, -selectorSize / 2.0f); const f32v2 textScale((((selectorSize - MainMenuSystemViewer::MIN_SELECTOR_SIZE) / (MainMenuSystemViewer::MAX_SELECTOR_SIZE - MainMenuSystemViewer::MIN_SELECTOR_SIZE)) * 0.5f + 0.5f) * 0.6f); // Draw Text if (textColor.a > 0) { m_spriteBatch->drawString(m_spriteFont, npCmp.name.c_str(), xyScreenCoords + textOffset, textScale, textColor, vg::TextAlign::TOP_LEFT, screenCoords.z); } } // Land selector if (isSelected && bodyArData->isLandSelected) { f32v3 selectedPos = bodyArData->selectedPos; // Apply axis rotation if applicable vecs::ComponentID componentID = m_spaceSystem->axisRotation.getComponentID(it.first); if (componentID) { f64q rot = m_spaceSystem->axisRotation.get(componentID).currentOrientation; selectedPos = f32v3(rot * f64v3(selectedPos)); } relativePos = (position + f64v3(selectedPos)) - m_camera->getPosition(); // Bring it close to the camera so it doesn't get occluded by anything relativePos = glm::normalize(relativePos) * ((f64)m_camera->getNearClip() + 0.0001); screenCoords = m_camera->worldToScreenPointLogZ(relativePos, (f64)m_camera->getFarClip()); xyScreenCoords = f32v2(screenCoords.x * m_viewport.x, screenCoords.y * m_viewport.y); color4 sColor = color::Red; sColor.a = 155; m_spriteBatch->draw(m_selectorTexture, nullptr, nullptr, xyScreenCoords, f32v2(0.5f, 0.5f), f32v2(22.0f) + (cosf(dt * 8.0f) * 4.0f), dt * ROTATION_FACTOR, sColor, screenCoords.z); } } } m_spriteBatch->end(); m_spriteBatch->render(m_viewport, nullptr, &vg::DepthState::READ, nullptr); } ================================================ FILE: SoA/SystemARRenderer.h ================================================ /// /// SystemARRenderer.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 22 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Augmented reality renderer for Space Systems /// #pragma once #ifndef SystemARRenderer_h__ #define SystemARRenderer_h__ #include #include #include "OrbitComponentRenderer.h" #include class Camera; class MainMenuSystemViewer; class SpaceSystem; class ModPathResolver; DECL_VG(class SpriteBatch; class SpriteFont) class SystemARRenderer { public: SystemARRenderer(); ~SystemARRenderer(); void init(const ModPathResolver* textureResolver); void initGL(); void draw(SpaceSystem* spaceSystem, const Camera* camera, OPT const MainMenuSystemViewer* systemViewer, const f32v2& viewport); void dispose(); private: void loadTextures(); // Renders space paths void drawPaths(); // Renders heads up display void drawHUD(); vg::GLProgram m_colorProgram; vg::SpriteBatch* m_spriteBatch = nullptr; vg::SpriteFont* m_spriteFont = nullptr; // Helper variables to avoid passing const ModPathResolver* m_textureResolver = nullptr; SpaceSystem* m_spaceSystem = nullptr; const Camera* m_camera = nullptr; const MainMenuSystemViewer* m_systemViewer = nullptr; VGTexture m_selectorTexture = 0; VGTexture m_baryTexture = 0; f32v2 m_viewport; f32 m_zCoef; OrbitComponentRenderer m_orbitComponentRenderer; }; #endif // SystemARRenderer_h__ ================================================ FILE: SoA/SystemBodyLoader.cpp ================================================ #include "stdafx.h" #include "SystemBodyLoader.h" #include "SpaceSystemAssemblages.h" #include "SoAState.h" #include void SystemBodyLoader::init(vio::IOManager* iom) { m_iom = iom; m_planetLoader.init(iom); } // TODO: Check why glrpc is currently unused. bool SystemBodyLoader::loadBody(const SoaState* soaState, const nString& filePath, const SystemOrbitProperties* sysProps, SystemBody* body, keg::Node &value, vcore::RPCManager* glrpc VORB_UNUSED /* = nullptr */) { #define KEG_CHECK \ if (error != keg::Error::NONE) { \ fprintf(stderr, "keg error %d for %s\n", (int)error, filePath.c_str()); \ goodParse = false; \ return; \ } keg::Error error; nString data; nString propertiesFile=filePath+"properties.yml"; m_iom->readFileToString(propertiesFile.c_str(), data); keg::ReadContext context; context.env=keg::getGlobalEnvironment(); bool goodParse = true; bool foundOne = false; auto f = makeFunctor([&](Sender, const nString& type, keg::Node value) { if (foundOne) return; // Parse based on type if (type == "planet") { PlanetProperties properties; error = keg::parse((ui8*)&properties, value, context, &KEG_GLOBAL_TYPE(PlanetProperties)); KEG_CHECK; // Use planet loader to load terrain and biomes if (properties.generation.length()) { properties.planetGenData = m_planetLoader.loadPlanetGenData(properties.generation); } else { properties.planetGenData = nullptr; // properties.planetGenData = pr.planetLoader->getRandomGenData(properties.density, pr.glrpc); properties.atmosphere = m_planetLoader.getRandomAtmosphere(); } // Set the radius for use later if (properties.planetGenData) { properties.planetGenData->radius = properties.diameter / 2.0; } SpaceSystemAssemblages::createPlanet(soaState->spaceSystem, sysProps, &properties, body, soaState->threadPool); body->type = SpaceBodyType::PLANET; } else if (type == "star") { StarProperties properties; error = keg::parse((ui8*)&properties, value, context, &KEG_GLOBAL_TYPE(StarProperties)); KEG_CHECK; SpaceSystemAssemblages::createStar(soaState->spaceSystem, sysProps, &properties, body); body->type = SpaceBodyType::STAR; } else if (type == "gasGiant") { GasGiantProperties properties; error = keg::parse((ui8*)&properties, value, context, &KEG_GLOBAL_TYPE(GasGiantProperties)); KEG_CHECK; // Get full path for color map if (properties.colorMap.size()) { vio::Path colorPath; if (!m_iom->resolvePath(properties.colorMap, colorPath)) { fprintf(stderr, "Failed to resolve %s\n", properties.colorMap.c_str()); } properties.colorMap = colorPath.getString(); } // Get full path for rings if (properties.rings.size()) { for (size_t i = 0; i < properties.rings.size(); i++) { auto& r = properties.rings[i]; // Resolve the path vio::Path ringPath; if (!m_iom->resolvePath(r.colorLookup, ringPath)) { fprintf(stderr, "Failed to resolve %s\n", r.colorLookup.c_str()); } r.colorLookup = ringPath.getString(); } } // Create the component SpaceSystemAssemblages::createGasGiant(soaState->spaceSystem, sysProps, &properties, body); body->type = SpaceBodyType::GAS_GIANT; } //Only parse the first foundOne = true; }); if(data.empty()) { f.invoke(nullptr, spaceObjectTypeName(sysProps->type), value); } else { context.reader.init(data.c_str()); keg::Node node=context.reader.getFirst(); if(keg::getType(node)!=keg::NodeType::MAP) { //lets go ahead and give it an entity id even though we dont have properties for it body->entity=soaState->spaceSystem->addEntity(); std::cout<<"Failed to load "+filePath; context.reader.dispose(); return false; } context.reader.forAllInMap(node, &f); } context.reader.dispose(); return goodParse; } ================================================ FILE: SoA/SystemBodyLoader.h ================================================ /// /// SystemBodyLoader.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 12 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Loads a system body /// #pragma once #ifndef SystemBodyLoader_h__ #define SystemBodyLoader_h__ #include "PlanetGenLoader.h" #include struct SoaState; DECL_VIO(class IOManager); class SystemBodyLoader { public: void init(vio::IOManager* iom); bool loadBody(const SoaState* soaState, const nString& filePath, const SystemOrbitProperties* sysProps, SystemBody* body, keg::Node &value, vcore::RPCManager* glrpc = nullptr); private: vio::IOManager* m_iom; PlanetGenLoader m_planetLoader; }; #endif // SystemBodyLoader_h__ ================================================ FILE: SoA/TerrainGenTextures.cpp ================================================ #include "stdafx.h" #include "TerrainGenTextures.h" #include "Errors.h" #include #include TerrainGenTextures::~TerrainGenTextures() { destroy(); } void TerrainGenTextures::init(const ui32v2& dims) { m_dims = dims; glGenFramebuffers(1, &m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // Create texture targets glGenTextures(1, m_textures); initTarget(m_dims, m_tex.height_temp_hum, TERRAINGEN_INTERNAL_FORMAT, 0); // Set the output location for pixels VGEnum bufs[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, bufs); // Unbind used resources glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); // TODO: Change The Memory Usage Of The GPU } void TerrainGenTextures::use() { glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glViewport(0, 0, m_dims.x, m_dims.y); } void TerrainGenTextures::unuse() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } void TerrainGenTextures::destroy() { if (m_fbo != 0) { glDeleteFramebuffers(1, &m_fbo); m_fbo = 0; } if (m_tex.height_temp_hum != 0) { glDeleteTextures(1, m_textures); m_tex = { 0 }; } } void TerrainGenTextures::initTarget(const ui32v2& size, const ui32& texID, const vg::TextureInternalFormat& format, const ui32& attachment) { glBindTexture(GL_TEXTURE_2D, texID); glTexImage2D(GL_TEXTURE_2D, 0, (VGEnum)format, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); vg::SamplerState::POINT_CLAMP.set(GL_TEXTURE_2D); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachment, GL_TEXTURE_2D, texID, 0); } ================================================ FILE: SoA/TerrainGenTextures.h ================================================ /// /// TerrainGenTextures.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 18 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// The output textures for terrain generation /// #pragma once #ifndef TerrainGenTextures_h__ #define TerrainGenTextures_h__ #include #include #define TERRAINGEN_INTERNAL_FORMAT vg::TextureInternalFormat::RGBA32F class TerrainGenTextures { public: struct TextureIDs { public: VGTexture height_temp_hum; }; ~TerrainGenTextures(); void init(const ui32v2& dims); /// @return OpenGL texture IDs const TerrainGenTextures::TextureIDs& getTextureIDs() const { return m_tex; } void use(); static void unuse(); void destroy(); private: void initTarget(const ui32v2& size, const ui32& texID, const vg::TextureInternalFormat& format, const ui32& attachment); union { TextureIDs m_tex; ///< Named texture targets VGTexture m_textures[1]; ///< All 1 textures }; VGFramebuffer m_fbo = 0; ui32v2 m_dims = ui32v2(0); }; #endif // TerrainGenTextures_h__ ================================================ FILE: SoA/TerrainPatch.cpp ================================================ #include "stdafx.h" #include "TerrainPatch.h" #include "TerrainPatchMesher.h" #include "TerrainPatchMesh.h" #include "Chunk.h" #include #include "RenderUtils.h" #include "SpaceSystemComponents.h" #include "SphericalTerrainComponentUpdater.h" #include "TerrainPatchMeshTask.h" #include "VoxPool.h" #include "VoxelCoordinateSpaces.h" #include "VoxelSpaceConversions.h" #include "soaUtils.h" f32 TerrainPatch::DIST_MIN = 1.0f; f32 TerrainPatch::DIST_MAX = 1.1f; f32 TerrainPatch::MIN_SIZE = 0.4096f; int TerrainPatch::PATCH_MAX_LOD = 25; TerrainPatch::~TerrainPatch() { destroy(); } void TerrainPatch::init(const f64v2& gridPosition, WorldCubeFace cubeFace, int lod, const TerrainPatchData* sphericalTerrainData, f64 width) { m_gridPos = gridPosition; m_cubeFace = cubeFace; m_lod = lod; m_terrainPatchData = sphericalTerrainData; m_width = width; // Construct an approximate AABB const i32v3& coordMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)m_cubeFace]; const i32v2& coordMults = VoxelSpaceConversions::FACE_TO_WORLD_MULTS[(int)m_cubeFace]; f64v3 corners[4]; corners[0][coordMapping.x] = gridPosition.x * coordMults.x; corners[0][coordMapping.y] = m_terrainPatchData->radius * VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; corners[0][coordMapping.z] = gridPosition.y * coordMults.y; corners[0] = glm::normalize(corners[0]) * m_terrainPatchData->radius; corners[1][coordMapping.x] = gridPosition.x * coordMults.x; corners[1][coordMapping.y] = m_terrainPatchData->radius * VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; corners[1][coordMapping.z] = (gridPosition.y + m_width) * coordMults.y; corners[1] = glm::normalize(corners[1]) * m_terrainPatchData->radius; corners[2][coordMapping.x] = (gridPosition.x + m_width) * coordMults.x; corners[2][coordMapping.y] = m_terrainPatchData->radius * VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; corners[2][coordMapping.z] = (gridPosition.y + m_width) * coordMults.y; corners[2] = glm::normalize(corners[2]) * m_terrainPatchData->radius; corners[3][coordMapping.x] = (gridPosition.x + m_width) * coordMults.x; corners[3][coordMapping.y] = m_terrainPatchData->radius * VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; corners[3][coordMapping.z] = gridPosition.y * coordMults.y; corners[3] = glm::normalize(corners[3]) * m_terrainPatchData->radius; f64 minX = INT_MAX, maxX = INT_MIN; f64 minY = INT_MAX, maxY = INT_MIN; f64 minZ = INT_MAX, maxZ = INT_MIN; for (int i = 0; i < 4; i++) { auto& c = corners[i]; if (c.x < minX) minX = c.x; if (c.x > maxX) maxX = c.x; if (c.y < minY) minY = c.y; if (c.y > maxY) maxY = c.y; if (c.z < minZ) minZ = c.z; if (c.z > maxZ) maxZ = c.z; } // Get world position and bounding box m_aabbPos = f32v3(minX, minY, minZ); m_aabbDims = f32v3(maxX - minX, maxY - minY, maxZ - minZ); } void TerrainPatch::update(const f64v3& cameraPos) { // TODO(Matthew): This statement is not used, revisit this function to see if it's going its job correctly. // Calculate distance from camera //f64v3 closestPoint = calculateClosestPointAndDist(cameraPos); if (m_children) { // Check for out of range if (m_distance > m_width * DIST_MAX) { if (!m_mesh) { requestMesh(true); } else if (hasMesh()) { // Out of range, kill children delete[] m_children; m_children = nullptr; } } else if (m_mesh) { // In range, but we need to remove our mesh. // Check to see if all children are renderable bool deleteMesh = true; for (int i = 0; i < 4; i++) { if (!m_children[i].isRenderable()) { deleteMesh = false; break; } } if (deleteMesh) { // Children are renderable, free mesh. // Render thread will deallocate. m_mesh->m_shouldDelete = true; m_mesh = nullptr; } } } else if (canSubdivide()) { m_children = new TerrainPatch[4]; // Segment into 4 children for (int z = 0; z < 2; z++) { for (int x = 0; x < 2; x++) { m_children[(z << 1) + x].init(m_gridPos + f64v2((m_width / 2.0) * x, (m_width / 2.0) * z), m_cubeFace, m_lod + 1, m_terrainPatchData, m_width / 2.0); } } } else if (!m_mesh) { requestMesh(true); } // Recursively update children if they exist if (m_children) { for (int i = 0; i < 4; i++) { m_children[i].update(cameraPos); } } } void TerrainPatch::destroy() { if (m_mesh) { m_mesh->m_shouldDelete = true; m_mesh = nullptr; } delete[] m_children; m_children = nullptr; } bool TerrainPatch::hasMesh() const { return (m_mesh && m_mesh->m_isRenderable); } bool TerrainPatch::isRenderable() const { if (hasMesh()) return true; if (m_children) { for (int i = 0; i < 4; i++) { if (!m_children[i].isRenderable()) return false; } return true; } return false; } bool TerrainPatch::isOverHorizon(const f64v3 &relCamPos, const f64v3 &point, f64 planetRadius) { const f64 DELTA = 0.1; f64 camHeight = glm::length(relCamPos); f64v3 normalizedCamPos = relCamPos / camHeight; // Limit the camera depth if (camHeight < planetRadius + 1.0) camHeight = planetRadius + 1.0; f64 horizonAngle = acos(planetRadius / camHeight); f64 lodAngle = acos(glm::dot(normalizedCamPos, glm::normalize(point))); if (lodAngle >= horizonAngle + DELTA) { return true; } return false; } void TerrainPatch::setQuality(int quality) { // Prevent infinite loop memory allocation due to bad input if (quality < 0 || quality > 6) { fprintf(stderr, "ERROR: Bad terrain quality: %d", quality); return; } DIST_MIN = (f32)quality; DIST_MAX = DIST_MIN + 0.1f; PATCH_MAX_LOD = 22 + quality * 2; } bool TerrainPatch::canSubdivide() const { return (m_lod < PATCH_MAX_LOD && m_distance < m_width * DIST_MIN && m_width > MIN_SIZE); } void TerrainPatch::requestMesh(bool isSpherical) { f32v3 startPos(m_gridPos.x, m_terrainPatchData->radius, m_gridPos.y); m_mesh = new TerrainPatchMesh(m_cubeFace, isSpherical); TerrainPatchMeshTask* meshTask = new TerrainPatchMeshTask(); meshTask->init(m_terrainPatchData, m_mesh, startPos, (f32)m_width, m_cubeFace); m_terrainPatchData->threadPool->addTask(meshTask); } f64v3 TerrainPatch::calculateClosestPointAndDist(const f64v3& cameraPos) { f64v3 closestPoint; // TODO(Ben): The 0 is a temporary oscillation fix if (0 && hasMesh()) { // If we have a mesh, we can use it's accurate bounding box closestPoint = m_mesh->getClosestPoint(cameraPos); } else { closestPoint = getClosestPointOnAABB(cameraPos, m_aabbPos, m_aabbDims); } m_distance = glm::length(closestPoint - cameraPos); return closestPoint; } ================================================ FILE: SoA/TerrainPatch.h ================================================ /// /// TerrainPatch.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 15 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// A terrain patch for use with a SphericalTerrainComponent /// #pragma once #ifndef TerrainPatch_h__ #define TerrainPatch_h__ #include "VoxelCoordinateSpaces.h" #include "TerrainPatchConstants.h" #include "VoxPool.h" #include class TerrainPatchMesh; class TerrainPatchMesher; class SphericalHeightmapGenerator; class TerrainPatchMeshManager; // Shared data for terrain patches struct TerrainPatchData { // TODO(Ben): probably dont need this friend struct SphericalTerrainComponent; TerrainPatchData(f64 radius, f64 patchWidth, SphericalHeightmapGenerator* generator, TerrainPatchMeshManager* meshManager, vcore::ThreadPool* threadPool) : radius(radius), patchWidth(patchWidth), generator(generator), meshManager(meshManager), threadPool(threadPool) { // Empty } f64 radius; ///< Radius of the planet in KM f64 patchWidth; ///< Width of a patch in KM SphericalHeightmapGenerator* generator; TerrainPatchMeshManager* meshManager; vcore::ThreadPool* threadPool; }; // TODO(Ben): Sorting // fix redundant quality changes class TerrainPatch { public: TerrainPatch() { }; virtual ~TerrainPatch(); /// Initializes the patch /// @param gridPosition: Position on the 2d face grid /// @param sphericalTerrainData: Shared data /// @param width: Width of the patch in KM virtual void init(const f64v2& gridPosition, WorldCubeFace cubeFace, int lod, const TerrainPatchData* sphericalTerrainData, f64 width); /// Updates the patch /// @param cameraPos: Position of the camera virtual void update(const f64v3& cameraPos); /// Frees resources void destroy(); /// @return true if it has a generated mesh bool hasMesh() const; /// @return true if it has a mesh, or all of its children are /// renderable. bool isRenderable() const; static bool isOverHorizon(const f64v3 &relCamPos, const f64v3 &point, f64 planetRadius); static void setQuality(int quality); /// Returns true if the patch can subdivide bool canSubdivide() const; protected: /// Requests a mesh via RPC void requestMesh(bool isSpherical); /// Calculates the closest point to the camera, as well as distance /// @param cameraPos: position of the observer /// @return closest point on the AABB f64v3 calculateClosestPointAndDist(const f64v3& cameraPos); static f32 DIST_MIN; static f32 DIST_MAX; static f32 MIN_SIZE; static int PATCH_MAX_LOD; f64v2 m_gridPos = f64v2(0.0); ///< Position on 2D grid f64v3 m_aabbPos = f64v3(0.0); ///< Position relative to world f64v3 m_aabbDims = f64v3(0.0); f64 m_distance = 1000000000.0; ///< Distance from camera int m_lod = 0; ///< Level of detail WorldCubeFace m_cubeFace; ///< Which cube face grid it is on f64 m_width = 0.0; ///< Width of the patch in KM TerrainPatchMesh* m_mesh = nullptr; const TerrainPatchData* m_terrainPatchData = nullptr; ///< Shared data pointer TerrainPatch* m_children = nullptr; ///< Pointer to array of 4 children }; #endif // TerrainPatch_h__ ================================================ FILE: SoA/TerrainPatchConstants.h ================================================ /// /// TerrainPatchConstants.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 17 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Constants for terrain patch stuff /// #pragma once #ifndef TerrainPatchConstants_h__ #define TerrainPatchConstants_h__ const int PATCH_NORMALMAP_PIXELS_PER_QUAD = 4; ///< Pixels of normalMap per quad const int PATCH_WIDTH = 33; ///< Width of patches in vertices const int PADDED_PATCH_WIDTH = PATCH_WIDTH + 2; ///< Width of patches in vertices const int PATCH_SIZE = PATCH_WIDTH * PATCH_WIDTH; ///< Size of patches in vertices const int PATCH_NORMALMAP_WIDTH = (PATCH_WIDTH - 1) * PATCH_NORMALMAP_PIXELS_PER_QUAD + 2; ///< Width of normalmap in pixels, + 2 for padding const int PATCH_HEIGHTMAP_WIDTH = PATCH_NORMALMAP_WIDTH + 2; ///< Width of heightmap in pixels, + 2 for padding const int TEXELS_PER_PATCH = PATCH_NORMALMAP_WIDTH - 2; ///< The number of texels contained in a patch. const int NUM_SKIRTS = 4; const int INDICES_PER_QUAD = 6; ///< Indices used to render a quad with glDrawElements const int PATCH_INDICES = (PATCH_WIDTH - 1) * (PATCH_WIDTH - 1 + NUM_SKIRTS) * INDICES_PER_QUAD; ///< Total indices in a patch const int PATCH_INDICES_NO_SKIRTS = (PATCH_WIDTH - 1) * (PATCH_WIDTH - 1) * INDICES_PER_QUAD; ///< Total indices in a patch with no skirts #endif // TerrainPatchConstants_h__ ================================================ FILE: SoA/TerrainPatchMesh.cpp ================================================ #include "stdafx.h" #include "TerrainPatchMesh.h" #include #include #include #include "Camera.h" #include "RenderUtils.h" #include "soaUtils.h" #include "VoxelCoordinateSpaces.h" #include "VoxelSpaceConversions.h" TerrainPatchMesh::~TerrainPatchMesh() { if (m_vbo) { vg::GpuMemory::freeBuffer(m_vbo); } if (m_wvbo) { vg::GpuMemory::freeBuffer(m_wvbo); } if (m_wibo) { vg::GpuMemory::freeBuffer(m_wibo); } if (m_vao) { glDeleteVertexArrays(1, &m_vao); } if (m_wvao) { glDeleteVertexArrays(1, &m_wvao); } } void TerrainPatchMesh::draw(const f32m4& WVP, const vg::GLProgram& program, bool drawSkirts) const { glUniformMatrix4fv(program.getUniform("unWVP"), 1, GL_FALSE, &WVP[0][0]); glBindVertexArray(m_vao); if (drawSkirts) { glDrawElements(GL_TRIANGLES, PATCH_INDICES, GL_UNSIGNED_SHORT, 0); } else { glDrawElements(GL_TRIANGLES, PATCH_INDICES_NO_SKIRTS, GL_UNSIGNED_SHORT, 0); } glBindVertexArray(0); } void TerrainPatchMesh::drawWater(const f32m4& WVP, const vg::GLProgram& program) const { glUniformMatrix4fv(program.getUniform("unWVP"), 1, GL_FALSE, &WVP[0][0]); glBindVertexArray(m_wvao); glDrawElements(GL_TRIANGLES, m_waterIndexCount, GL_UNSIGNED_SHORT, 0); glBindVertexArray(0); } void TerrainPatchMesh::drawAsFarTerrain(const f64v3& relativePos, const f32m4& VP, const vg::GLProgram& program, bool drawSkirts) const { // No need for matrix with simple translation f32v3 translation = f32v3(f64v3(m_aabbPos) - relativePos); glUniformMatrix4fv(program.getUniform("unVP"), 1, GL_FALSE, &VP[0][0]); glUniform3fv(program.getUniform("unTranslation"), 1, &translation[0]); glUniform3fv(program.getUniform("unPosition"), 1, &m_aabbPos[0]); glBindVertexArray(m_vao); if (drawSkirts) { glDrawElements(GL_TRIANGLES, PATCH_INDICES, GL_UNSIGNED_SHORT, 0); } else { glDrawElements(GL_TRIANGLES, PATCH_INDICES_NO_SKIRTS, GL_UNSIGNED_SHORT, 0); } glBindVertexArray(0); } /// Draws the water mesh as a far terrain mesh void TerrainPatchMesh::drawWaterAsFarTerrain(const f64v3& relativePos, const f32m4& VP, const vg::GLProgram& program) const { // No need for matrix with simple translation f32v3 translation = f32v3(f64v3(m_aabbPos) - relativePos); glUniformMatrix4fv(program.getUniform("unVP"), 1, GL_FALSE, &VP[0][0]); glUniform3fv(program.getUniform("unTranslation"), 1, &translation[0]); glUniform3fv(program.getUniform("unPosition"), 1, &m_aabbPos[0]); glBindVertexArray(m_wvao); glDrawElements(GL_TRIANGLES, m_waterIndexCount, GL_UNSIGNED_SHORT, 0); glBindVertexArray(0); } f32v3 TerrainPatchMesh::getClosestPoint(const f32v3& camPos) const { return getClosestPointOnAABB(camPos, m_aabbPos, m_aabbDims); } f64v3 TerrainPatchMesh::getClosestPoint(const f64v3& camPos) const { return getClosestPointOnAABB(camPos, f64v3(m_aabbPos), f64v3(m_aabbDims)); } ================================================ FILE: SoA/TerrainPatchMesh.h ================================================ /// /// TerrainPatchMesh.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 17 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Defines mesh for TerrainPatches /// #pragma once #ifndef TerrainPatchMesh_h__ #define TerrainPatchMesh_h__ #include #include #include "VoxelCoordinateSpaces.h" #include "TerrainPatchConstants.h" class Camera; DECL_VG(class TextureRecycler); DECL_VG(class GLProgram); /// Vertex for terrain patch class TerrainVertex { public: f32v3 position; //12 f32v3 normal; //24 ColorRGB8 color; //27 ui8 padding; //28 ui8 temperature; //29 ui8 humidity; //30 ui8 padding2[2]; //32 }; /// Water vertex for terrain patch class WaterVertex { public: f32v3 position; //12 f32v3 tangent; //24 ColorRGB8 color; //27 ui8 temperature; //28 float depth; //32 }; class TerrainPatchMesh { public: friend class FarTerrainPatch; friend class TerrainPatch; friend class TerrainPatchMeshManager; friend class TerrainPatchMeshTask; friend class TerrainPatchMesher; TerrainPatchMesh(WorldCubeFace cubeFace, bool isSpherical) : m_cubeFace(cubeFace), m_isSpherical(isSpherical) {} ~TerrainPatchMesh(); /// Recycles the normal map /// @param recycler: Recycles the texture void recycleNormalMap(vg::TextureRecycler* recycler); /// Draws the terrain mesh void draw(const f32m4& WVP, const vg::GLProgram& program, bool drawSkirts) const; /// Draws the water mesh void drawWater(const f32m4& WVP, const vg::GLProgram& program) const; /// Draws the terrain mesh as a far terrain mesh void drawAsFarTerrain(const f64v3& relativePos, const f32m4& VP, const vg::GLProgram& program, bool drawSkirts) const; /// Draws the water mesh as a far terrain mesh void drawWaterAsFarTerrain(const f64v3& relativePos, const f32m4& VP, const vg::GLProgram& program) const; /// Gets the point closest to the observer /// @param camPos: Position of observer /// @return the closest point on the aabb f32v3 getClosestPoint(const f32v3& camPos) const; f64v3 getClosestPoint(const f64v3& camPos) const; const bool& getIsSpherical() const { return m_isSpherical; } f64 distance2 = 100000000000.0; private: VGVertexArray m_vao = 0; ///< Vertex array object VGVertexBuffer m_vbo = 0; ///< Vertex buffer object VGVertexArray m_wvao = 0; ///< Water vertex array object VGVertexBuffer m_wvbo = 0; ///< Water Vertex buffer object VGIndexBuffer m_wibo = 0; ///< Water Index Buffer Object f32v3 m_aabbPos = f32v3(0.0f); ///< Bounding box origin f32v3 m_aabbDims = f32v3(0.0f); ///< Bounding box dims f32v3 m_aabbCenter = f32v3(0.0f); ///< Center of the bounding box f32 m_boundingSphereRadius = 0.0f; ///< Radius of sphere for frustum checks WorldCubeFace m_cubeFace; std::vector m_meshDataBuffer; ///< Stores mesh data for terrain and water in bytes int m_waterIndexCount = 0; int m_waterVertexCount = 0; volatile bool m_shouldDelete = false; ///< True when the mesh should be deleted bool m_isRenderable = false; ///< True when there is a complete mesh bool m_isSpherical = false; }; #endif // TerrainPatchMesh_h__ ================================================ FILE: SoA/TerrainPatchMeshManager.cpp ================================================ #include "stdafx.h" #include "TerrainPatchMeshManager.h" #include "Errors.h" #include "PlanetGenLoader.h" #include "Camera.h" #include #include #include "FarTerrainPatch.h" #include "PlanetGenData.h" #include "RenderUtils.h" #include "SpaceSystemComponents.h" #include "TerrainPatch.h" #include "TerrainPatchMesh.h" #include "soaUtils.h" #define MAX_UPDATES_PER_FRAME 100 void TerrainPatchMeshManager::update() { TerrainPatchMesh* meshes[MAX_UPDATES_PER_FRAME]; if (size_t numUpdates = m_meshesToAdd.try_dequeue_bulk(meshes, MAX_UPDATES_PER_FRAME)) { for (size_t i = 0; i < numUpdates; i++) { addMesh(meshes[i]); } } } void TerrainPatchMeshManager::drawSphericalMeshes(const f64v3& relativePos, const Camera* camera, const f64q& orientation, vg::GLProgram& program, vg::GLProgram& waterProgram, const f32v3& lightDir, f32 alpha, const f32 zCoef, const AtmosphereComponent* aCmp, bool drawSkirts) { static f32 dt = 0.0f; dt += 0.00003f; f64q invOrientation = glm::inverse(orientation); const f64v3 rotpos = invOrientation * relativePos; const f32v3 rotLightDir = f32v3(invOrientation * f64v3(lightDir)); // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)orientation.x; orientationF32.y = (f32)orientation.y; orientationF32.z = (f32)orientation.z; orientationF32.w = (f32)orientation.w; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); f32m4 W(1.0); setMatrixTranslation(W, -relativePos); f32m4 WVP = camera->getViewProjectionMatrix() * W * rotationMatrix; if (m_waterMeshes.size()) { // Bind textures glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_planetGenData->liquidColorMap.id); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_planetGenData->liquidTexture.id); waterProgram.use(); waterProgram.enableVertexAttribArrays(); // Set uniforms glUniform1f(waterProgram.getUniform("unDt"), dt); glUniform1f(waterProgram.getUniform("unDepthScale"), m_planetGenData->liquidDepthScale); glUniform1f(waterProgram.getUniform("unFreezeTemp"), m_planetGenData->liquidFreezeTemp / 255.0f); glUniform3fv(waterProgram.getUniform("unLightDirWorld"), 1, &rotLightDir[0]); glUniform1f(waterProgram.getUniform("unAlpha"), alpha); // For logarithmic Z buffer glUniform1f(waterProgram.getUniform("unZCoef"), zCoef); // Set up scattering uniforms setScatterUniforms(waterProgram, rotpos, aCmp); for (size_t i = 0; i < m_waterMeshes.size();) { auto& m = m_waterMeshes[i]; if (m->m_shouldDelete) { // Only delete here if m_wvbo is 0. See comment [15] in below block if (m->m_wvbo) { vg::GpuMemory::freeBuffer(m->m_wvbo); } else { delete m; } m = m_waterMeshes.back(); m_waterMeshes.pop_back(); } else { // TODO(Ben): Horizon and frustum culling for water too m->drawWater(WVP, waterProgram); i++; } } waterProgram.disableVertexAttribArrays(); waterProgram.unuse(); } if (m_meshes.size()) { // Bind textures glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_planetGenData->terrainColorMap.id); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_planetGenData->grassTexture.id); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, m_planetGenData->rockTexture.id); glActiveTexture(GL_TEXTURE0); program.use(); program.enableVertexAttribArrays(); // For logarithmic Z buffer glUniform1f(program.getUniform("unZCoef"), zCoef); glUniform3fv(program.getUniform("unLightDirWorld"), 1, &rotLightDir[0]); glUniform1f(program.getUniform("unAlpha"), alpha); // Set up scattering uniforms setScatterUniforms(program, rotpos, aCmp); for (size_t i = 0; i < m_meshes.size();) { auto& m = m_meshes[i]; if (m->m_shouldDelete) { // [15] If m_wvbo is 1, then chunk was marked for delete between // Drawing water and terrain. So we free m_wvbo to mark it // for delete on the next pass through m_waterMeshes if (m->m_wvbo) { vg::GpuMemory::freeBuffer(m->m_wvbo); } else { delete m; } m = m_meshes.back(); m_meshes.pop_back(); } else { /// Use bounding box to find closest point f64v3 closestPoint = m->getClosestPoint(rotpos); // Check horizon culling first, it's more likely to cull spherical patches if (!TerrainPatch::isOverHorizon(rotpos, closestPoint, m_planetGenData->radius)) { // Check frustum culling // TODO(Ben): There could be a way to reduce the number of frustum checks // via caching or checking a parent f32v3 relSpherePos = orientationF32 * m->m_aabbCenter - f32v3(relativePos); if (camera->sphereInFrustum(relSpherePos, m->m_boundingSphereRadius)) { m->draw(WVP, program, drawSkirts); } } i++; } } program.disableVertexAttribArrays(); program.unuse(); } } TerrainPatchMeshManager::~TerrainPatchMeshManager() { for (auto& i : m_meshes) { delete i; } for (auto& i : m_farMeshes) { delete i; } } void TerrainPatchMeshManager::addMesh(TerrainPatchMesh* mesh) { // Upload data if (mesh->m_meshDataBuffer.size()) { TerrainPatchMesher::uploadMeshData(mesh); } // Add to mesh list if (mesh->getIsSpherical()) { m_meshes.push_back(mesh); if (mesh->m_wvbo) { m_waterMeshes.push_back(mesh); } } else { m_farMeshes.push_back(mesh); if (mesh->m_wvbo) { m_farWaterMeshes.push_back(mesh); } } mesh->m_isRenderable = true; } void TerrainPatchMeshManager::addMeshAsync(TerrainPatchMesh* mesh) { m_meshesToAdd.enqueue(mesh); } bool meshComparator(TerrainPatchMesh* m1, TerrainPatchMesh* m2) { return (m1->distance2 < m2->distance2); } void TerrainPatchMeshManager::sortSpericalMeshes(const f64v3& relPos) { // Calculate squared distances for (auto& mesh : m_meshes) { f64v3 distVec = mesh->getClosestPoint(relPos) - relPos; mesh->distance2 = selfDot(distVec); } // Not sorting water since it would be of minimal benifit std::sort(m_meshes.begin(), m_meshes.end(), [](TerrainPatchMesh* m1, TerrainPatchMesh* m2) -> bool { return (m1->distance2 < m2->distance2); }); } void TerrainPatchMeshManager::sortFarMeshes(const f64v3& relPos) { // Calculate squared distances for (auto& mesh : m_farMeshes) { f64v3 distVec = mesh->getClosestPoint(relPos) - relPos; mesh->distance2 = selfDot(distVec); } // Not sorting water since it would be of minimal benifit std::sort(m_farMeshes.begin(), m_farMeshes.end(), [](TerrainPatchMesh* m1, TerrainPatchMesh* m2) -> bool { return (m1->distance2 < m2->distance2); }); } void TerrainPatchMeshManager::drawFarMeshes(const f64v3& relativePos, const Camera* camera, vg::GLProgram& program, vg::GLProgram& waterProgram, const f32v3& lightDir, f32 alpha, f32 radius, const f32 zCoef, const AtmosphereComponent* aCmp, bool drawSkirts) { // TODO(Ben): This will lose precision over time static f32 dt = 0.0f; dt += 0.0001f; if (m_farWaterMeshes.size()) { // Bind textures glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_planetGenData->liquidColorMap.id); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_planetGenData->liquidTexture.id); waterProgram.use(); waterProgram.enableVertexAttribArrays(); // Set uniforms glUniform1f(waterProgram.getUniform("unDt"), dt); glUniform1f(waterProgram.getUniform("unDepthScale"), m_planetGenData->liquidDepthScale); glUniform1f(waterProgram.getUniform("unFreezeTemp"), m_planetGenData->liquidFreezeTemp / 255.0f); glUniform1f(waterProgram.getUniform("unRadius"), radius); glUniform3fv(waterProgram.getUniform("unLightDirWorld"), 1, &lightDir[0]); glUniform1f(waterProgram.getUniform("unAlpha"), alpha); // For logarithmic Z buffer glUniform1f(waterProgram.getUniform("unZCoef"), zCoef); // Set up scattering uniforms setScatterUniforms(waterProgram, f64v3(0, relativePos.y + radius, 0), aCmp); for (size_t i = 0; i < m_farWaterMeshes.size();) { auto& m = m_farWaterMeshes[i]; if (m->m_shouldDelete) { // Only delete here if m_wvbo is 0. See comment [15] in below block if (m->m_wvbo) { vg::GpuMemory::freeBuffer(m->m_wvbo); } else { delete m; } m = m_farWaterMeshes.back(); m_farWaterMeshes.pop_back(); } else { m->drawWaterAsFarTerrain(relativePos, camera->getViewProjectionMatrix(), waterProgram); i++; } } waterProgram.disableVertexAttribArrays(); waterProgram.unuse(); } if (m_farMeshes.size()) { // Bind textures glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_planetGenData->terrainColorMap.id); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_planetGenData->grassTexture.id); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, m_planetGenData->rockTexture.id); glActiveTexture(GL_TEXTURE0); program.use(); program.enableVertexAttribArrays(); glUniform1f(program.getUniform("unRadius"), radius); // TODO(Ben): Use real radius glUniform3fv(program.getUniform("unLightDirWorld"), 1, &lightDir[0]); glUniform1f(program.getUniform("unAlpha"), alpha); // For logarithmic Z buffer glUniform1f(program.getUniform("unZCoef"), zCoef); // Set up scattering uniforms setScatterUniforms(program, f64v3(0, relativePos.y + radius, 0), aCmp); for (size_t i = 0; i < m_farMeshes.size();) { auto& m = m_farMeshes[i]; if (m->m_shouldDelete) { // [15] If m_wvbo is 1, then chunk was marked for delete between // Drawing water and terrain. So we free m_wvbo to mark it // for delete on the next pass through m_farWaterMeshes if (m->m_wvbo) { vg::GpuMemory::freeBuffer(m->m_wvbo); } else { delete m; } m = m_farMeshes.back(); m_farMeshes.pop_back(); } else { // Check frustum culling // TODO(Ben): There could be a way to reduce the number of frustum checks // via caching or checking a parent // Check frustum culling first, it's more likely to cull far patches f32v3 relSpherePos = m->m_aabbCenter - f32v3(relativePos); if (camera->sphereInFrustum(relSpherePos, m->m_boundingSphereRadius)) { /// Use bounding box to find closest point f64v3 closestPoint = m->getClosestPoint(relativePos); if (!FarTerrainPatch::isOverHorizon(relativePos, closestPoint, m_planetGenData->radius)) { m->drawAsFarTerrain(relativePos, camera->getViewProjectionMatrix(), program, drawSkirts); } } i++; } } program.disableVertexAttribArrays(); program.unuse(); } } void TerrainPatchMeshManager::setScatterUniforms(vg::GLProgram& program, const f64v3& relPos, const AtmosphereComponent* aCmp) { // Set up scattering uniforms if (aCmp) { f32v3 relPosF(relPos); f32 camHeight = glm::length(relPosF); glUniform3fv(program.getUniform("unCameraPos"), 1, &relPosF[0]); glUniform3fv(program.getUniform("unInvWavelength"), 1, &aCmp->invWavelength4[0]); glUniform1f(program.getUniform("unCameraHeight2"), camHeight * camHeight); glUniform1f(program.getUniform("unInnerRadius"), aCmp->planetRadius); glUniform1f(program.getUniform("unOuterRadius"), aCmp->radius); glUniform1f(program.getUniform("unOuterRadius2"), aCmp->radius * aCmp->radius); glUniform1f(program.getUniform("unKrESun"), aCmp->kr * aCmp->esun); glUniform1f(program.getUniform("unKmESun"), aCmp->km * aCmp->esun); glUniform1f(program.getUniform("unKr4PI"), (f32)(aCmp->kr * M_4_PI)); glUniform1f(program.getUniform("unKm4PI"), (f32)(aCmp->km * M_4_PI)); f32 scale = 1.0f / (aCmp->radius - aCmp->planetRadius); glUniform1f(program.getUniform("unScale"), scale); glUniform1f(program.getUniform("unScaleDepth"), aCmp->scaleDepth); glUniform1f(program.getUniform("unScaleOverScaleDepth"), scale / aCmp->scaleDepth); glUniform1i(program.getUniform("unNumSamples"), 3); glUniform1f(program.getUniform("unNumSamplesF"), 3.0f); glUniform1f(program.getUniform("unG"), aCmp->g); glUniform1f(program.getUniform("unG2"), aCmp->g * aCmp->g); } } ================================================ FILE: SoA/TerrainPatchMeshManager.h ================================================ /// /// TerrainPatchMeshManager.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 17 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Simple container for terrain meshes. Each planet gets one /// #include "TerrainPatch.h" #pragma once #ifndef TerrainPatchMeshManager_h__ #define TerrainPatchMeshManager_h__ #include #include class Camera; class TerrainPatchMesh; struct AtmosphereComponent; struct PlanetGenData; struct TerrainPatchData; DECL_VG(class TextureRecycler; class GLProgram) class TerrainPatchMeshManager { public: TerrainPatchMeshManager(const PlanetGenData* planetGenData) : m_planetGenData(planetGenData){ // Empty } ~TerrainPatchMeshManager(); void update(); /// Draws the spherical meshes /// @param relativePos: Relative position of the camera /// @param Camera: The camera /// @param orientation: Orientation quaternion /// @param program: Shader program for rendering terrain /// @param waterProgram: Shader program for rendering water /// @param lightDir: Normalized direction to light source /// @param aCmp: Atmosphere component for rendering /// @param drawSkirts: True when you want to also draw skirts void drawSphericalMeshes(const f64v3& relativePos, const Camera* camera, const f64q& orientation, vg::GLProgram& program, vg::GLProgram& waterProgram, const f32v3& lightDir, f32 alpha, const f32 zCoef, const AtmosphereComponent* aCmp, bool drawSkirts); /// Draws the far meshes /// @param relativePos: Relative position of the camera /// @param Camera: The camera state /// @param orientation: Orientation quaternion /// @param program: Shader program for rendering terrain /// @param waterProgram: Shader program for rendering water /// @param lightDir: Normalized direction to light source /// @param radius: Radius of the planet in KM /// @param aCmp: Atmosphere component for rendering /// @param drawSkirts: True when you want to also draw skirts void drawFarMeshes(const f64v3& relativePos, const Camera* camera, vg::GLProgram& program, vg::GLProgram& waterProgram, const f32v3& lightDir, f32 alpha, f32 radius, const f32 zCoef, const AtmosphereComponent* aCmp, bool drawSkirts); /// Adds a mesh void addMesh(TerrainPatchMesh* mesh); /// Adds a mesh from a worker thread void addMeshAsync(TerrainPatchMesh* mesh); /// Updates distances and Sorts meshes void sortSpericalMeshes(const f64v3& relPos); /// Updates distances and Sorts meshes void sortFarMeshes(const f64v3& relPos); private: void setScatterUniforms(vg::GLProgram& program, const f64v3& relPos, const AtmosphereComponent* aCmp); moodycamel::ConcurrentQueue m_meshesToAdd; const PlanetGenData* m_planetGenData = nullptr; ///< Planetary data std::vector m_meshes; ///< All meshes std::vector m_waterMeshes; ///< Meshes with water active std::vector m_farMeshes; ///< All meshes std::vector m_farWaterMeshes; ///< Meshes with water active }; #endif // TerrainPatchMeshManager_h__ ================================================ FILE: SoA/TerrainPatchMeshTask.cpp ================================================ #include "stdafx.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatchMesh.h" #include "TerrainPatchMeshManager.h" #include "TerrainPatchMeshTask.h" #include "TerrainPatchMesher.h" #include "VoxelSpaceConversions.h" void TerrainPatchMeshTask::init(const TerrainPatchData* patchData, TerrainPatchMesh* mesh, const f32v3& startPos, float width, WorldCubeFace cubeFace) { m_patchData = patchData; m_mesh = mesh; m_startPos = startPos; m_width = width; m_cubeFace = cubeFace; } void TerrainPatchMeshTask::execute(WorkerData* workerData) { PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]; f64v3 positionData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]; f64v3 pos; // f32v3 tmpPos; SphericalHeightmapGenerator* generator = m_patchData->generator; if (m_mesh->m_shouldDelete) { delete m_mesh; return; } const float VERT_WIDTH = m_width / (PATCH_WIDTH - 1); bool isSpherical = m_mesh->getIsSpherical(); if (isSpherical) { const i32v3& coordMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)m_cubeFace]; const f32v2& coordMults = f32v2(VoxelSpaceConversions::FACE_TO_WORLD_MULTS[(int)m_cubeFace]); m_startPos.y *= (f32)VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; for (int z = 0; z < PADDED_PATCH_WIDTH; z++) { for (int x = 0; x < PADDED_PATCH_WIDTH; x++) { pos[coordMapping.x] = (m_startPos.x + (x - 1) * VERT_WIDTH) * coordMults.x; pos[coordMapping.y] = m_startPos.y; pos[coordMapping.z] = (m_startPos.z + (z - 1) * VERT_WIDTH) * coordMults.y; f64v3 normal(glm::normalize(pos)); generator->generateHeightData(heightData[z][x], normal); // offset position by height; positionData[z][x] = normal * (m_patchData->radius + heightData[z][x].height * KM_PER_VOXEL); } } } else { // Far terrain const i32v3& coordMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)m_cubeFace]; const f32v2& coordMults = f32v2(VoxelSpaceConversions::FACE_TO_WORLD_MULTS[(int)m_cubeFace]); m_startPos.y *= (f32)VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; for (int z = 0; z < PADDED_PATCH_WIDTH; z++) { for (int x = 0; x < PADDED_PATCH_WIDTH; x++) { f64v2 spos; spos.x = (m_startPos.x + (x - 1) * VERT_WIDTH); spos.y = (m_startPos.z + (z - 1) * VERT_WIDTH); pos[coordMapping.x] = spos.x * coordMults.x; pos[coordMapping.y] = m_startPos.y; pos[coordMapping.z] = spos.y * coordMults.y; f64v3 normal(glm::normalize(pos)); generator->generateHeightData(heightData[z][x], normal); // offset position by height; positionData[z][x] = f64v3(spos.x, heightData[z][x].height * KM_PER_VOXEL, spos.y); } } } // Check for early delete if (m_mesh->m_shouldDelete) { delete m_mesh; return; } if (!workerData->terrainMesher) workerData->terrainMesher = new TerrainPatchMesher(); workerData->terrainMesher->generateMeshData(m_mesh, generator->getGenData(), m_startPos, m_cubeFace, m_width, heightData, positionData); // Finally, add to the mesh manager m_patchData->meshManager->addMeshAsync(m_mesh); } ================================================ FILE: SoA/TerrainPatchMeshTask.h ================================================ /// /// TerrainPatchMeshTask.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Task for terrain patch mesh /// #pragma once #ifndef TerrainPatchMeshTask_h__ #define TerrainPatchMeshTask_h__ #include #include "Constants.h" #include "VoxPool.h" #include "VoxelCoordinateSpaces.h" struct TerrainPatchData; class TerrainPatchMesh; class TerrainPatchMesher; class TerrainPatchMeshManager; class SphericalHeightmapGenerator; #define TERRAIN_MESH_TASK_ID 6 // Represents A Mesh Creation Task class TerrainPatchMeshTask : public vcore::IThreadPoolTask < WorkerData > { public: TerrainPatchMeshTask() : vcore::IThreadPoolTask(TERRAIN_MESH_TASK_ID) {} // Initializes the task void init(const TerrainPatchData* patchData, TerrainPatchMesh* mesh, const f32v3& startPos, float width, WorldCubeFace cubeFace); // Executes the task void execute(WorkerData* workerData) override; private: f32v3 m_startPos; WorldCubeFace m_cubeFace; float m_width; TerrainPatchMesh* m_mesh = nullptr; const TerrainPatchData* m_patchData = nullptr; }; #endif // TerrainPatchMeshTask_h__ ================================================ FILE: SoA/TerrainPatchMesher.cpp ================================================ #include "stdafx.h" #include "TerrainPatchMesher.h" #include "VoxelSpaceConversions.h" #include "SphericalTerrainComponentUpdater.h" #include "SphericalHeightmapGenerator.h" #include "TerrainPatchMeshManager.h" #include "PlanetGenData.h" #include #include #include /// Debug colors for rendering faces with unique color const color3 DebugColors[6] { color3(255, 0, 0), //TOP color3(0, 255, 0), //LEFT color3(0, 0, 255), //RIGHT color3(255, 255, 0), //FRONT color3(0, 255, 255), //BACK color3(255, 0, 255) //BOTTOM }; VGIndexBuffer TerrainPatchMesher::m_sharedIbo = 0; ///< Reusable CCW IBO void TerrainPatchMesher::generateIndices() { // Loop through each quad and set indices int vertIndex; int index = 0; int skirtIndex = PATCH_SIZE; ui16 indices[PATCH_INDICES]; // Main vertices for (int z = 0; z < PATCH_WIDTH - 1; z++) { for (int x = 0; x < PATCH_WIDTH - 1; x++) { // Compute index of back left vertex vertIndex = z * PATCH_WIDTH + x; // Change triangle orientation based on odd or even if ((x + z) % 2) { indices[index++] = vertIndex; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = vertIndex + PATCH_WIDTH + 1; indices[index++] = vertIndex + PATCH_WIDTH + 1; indices[index++] = vertIndex + 1; indices[index++] = vertIndex; } else { indices[index++] = vertIndex + 1; indices[index++] = vertIndex; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = vertIndex + PATCH_WIDTH + 1; indices[index++] = vertIndex + 1; } } } // Skirt vertices // Top Skirt for (int i = 0; i < PATCH_WIDTH - 1; i++) { vertIndex = i; indices[index++] = skirtIndex; indices[index++] = vertIndex; indices[index++] = vertIndex + 1; indices[index++] = vertIndex + 1; indices[index++] = skirtIndex + 1; indices[index++] = skirtIndex; skirtIndex++; } skirtIndex++; // Skip last vertex // Left Skirt for (int i = 0; i < PATCH_WIDTH - 1; i++) { vertIndex = i * PATCH_WIDTH; indices[index++] = skirtIndex; indices[index++] = skirtIndex + 1; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = vertIndex; indices[index++] = skirtIndex; skirtIndex++; } skirtIndex++; // Skip last vertex // Right Skirt for (int i = 0; i < PATCH_WIDTH - 1; i++) { vertIndex = i * PATCH_WIDTH + PATCH_WIDTH - 1; indices[index++] = vertIndex; indices[index++] = vertIndex + PATCH_WIDTH; indices[index++] = skirtIndex + 1; indices[index++] = skirtIndex + 1; indices[index++] = skirtIndex; indices[index++] = vertIndex; skirtIndex++; } skirtIndex++; // Bottom Skirt for (int i = 0; i < PATCH_WIDTH - 1; i++) { vertIndex = PATCH_SIZE - PATCH_WIDTH + i; indices[index++] = vertIndex; indices[index++] = skirtIndex; indices[index++] = skirtIndex + 1; indices[index++] = skirtIndex + 1; indices[index++] = vertIndex + 1; indices[index++] = vertIndex; skirtIndex++; } vg::GpuMemory::createBuffer(m_sharedIbo); vg::GpuMemory::bindBuffer(m_sharedIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(m_sharedIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, PATCH_INDICES * sizeof(ui16), indices); } void TerrainPatchMesher::destroyIndices() { vg::GpuMemory::freeBuffer(m_sharedIbo); } void TerrainPatchMesher::generateMeshData(TerrainPatchMesh* mesh, const PlanetGenData* planetGenData, const f32v3& startPos, WorldCubeFace cubeFace, float width, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH], f64v3 positionData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]) { assert(m_sharedIbo != 0); m_planetGenData = planetGenData; m_radius = (f32)m_planetGenData->radius; m_isSpherical = mesh->getIsSpherical(); m_cubeFace = cubeFace; // Grab mappings so we can rotate the 2D grid appropriately if (m_isSpherical) { m_coordMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)m_cubeFace]; m_startPos = startPos; m_coordMults = f32v2(VoxelSpaceConversions::FACE_TO_WORLD_MULTS[(int)m_cubeFace]); } else { m_coordMapping = i32v3(0, 1, 2); m_startPos = f32v3(startPos.x, 0.0f, startPos.z); m_coordMults = f32v2(1.0f); } f32 h; // f32v3 tmpPos; f32 minX = FLT_MAX, maxX = -FLT_MAX; f32 minY = FLT_MAX, maxY = -FLT_MAX; f32 minZ = FLT_MAX, maxZ = -FLT_MAX; // Clear water index grid memset(waterIndexGrid, 0, sizeof(waterIndexGrid)); memset(waterQuads, 0, sizeof(waterQuads)); m_waterIndex = 0; m_waterIndexCount = 0; // Loop through and set all vertex attributes m_vertWidth = width / (PATCH_WIDTH - 1); m_index = 0; for (int z = 1; z < PADDED_PATCH_WIDTH - 1; z++) { for (int x = 1; x < PADDED_PATCH_WIDTH - 1; x++) { auto& v = verts[m_index]; // Set the position based on which face we are on v.position = positionData[z][x]; // Set color v.color = m_planetGenData->terrainTint; //v.color = DebugColors[(int)mesh->m_cubeFace]; // Uncomment for unique face colors // TODO(Ben): This is temporary edge debugging stuff /* const float delta = 100.0f; if (abs(v.position[m_coordMapping.x]) >= m_radius - delta || abs(v.position[m_coordMapping.z]) >= m_radius - delta) { v.color.r = 255; v.color.g = 0; v.color.b = 0; }*/ // TODO(Ben): This is temporary biome debugging // v.color = heightData[z][x].biome->mapColor; // Get data from heightmap h = heightData[z][x].height; // Water indexing if (h < 0) { addWater(z - 1, x - 1, heightData); } // TODO(Ben): Only update when not in frustum. Use double frustum method to start loading at frustum 2 and force in frustum 1 v.temperature = heightData[z][x].temperature; v.humidity = heightData[z][x].humidity; // Check bounding box // TODO(Ben): Worry about water too! if (v.position.x < minX) minX = v.position.x; if (v.position.x > maxX) maxX = v.position.x; if (v.position.y < minY) minY = v.position.y; if (v.position.y > maxY) maxY = v.position.y; if (v.position.z < minZ) minZ = v.position.z; if (v.position.z > maxZ) maxZ = v.position.z; m_index++; } } f64v3 pl; f64v3 pr; f64v3 pb; f64v3 pf; // Second pass for normals TODO(Ben): Padding for (int z = 1; z < PADDED_PATCH_WIDTH - 1; z++) { for (int x = 1; x < PADDED_PATCH_WIDTH - 1; x++) { auto& v = verts[(z - 1) * PATCH_WIDTH + x - 1]; f64v3& p = positionData[z][x]; pl = positionData[z][x - 1] - p; pr = positionData[z][x + 1] - p; pb = positionData[z - 1][x] - p; pf = positionData[z + 1][x] - p; // Calculate smooth normal v.normal = glm::normalize(glm::cross(pb, pl) + glm::cross(pl, pf) + glm::cross(pf, pr) + glm::cross(pr, pb)); } } // Get AABB mesh->m_aabbPos = f32v3(minX, minY, minZ); mesh->m_aabbDims = f32v3(maxX - minX, maxY - minY, maxZ - minZ); mesh->m_aabbCenter = mesh->m_aabbPos + mesh->m_aabbDims * 0.5f; // Calculate bounding sphere for culling mesh->m_boundingSphereRadius = glm::length(mesh->m_aabbCenter - mesh->m_aabbPos); // Build the skirts for crack hiding buildSkirts(); // Make all vertices relative to the aabb pos for far terrain if (!m_isSpherical) { for (int i = 0; i < m_index; i++) { verts[i].position = f32v3(f64v3(verts[i].position) - f64v3(mesh->m_aabbPos)); } } mesh->m_waterIndexCount = m_waterIndexCount; mesh->m_waterVertexCount = m_waterIndex; std::vector& data = mesh->m_meshDataBuffer; data.resize(VERTS_SIZE * sizeof(TerrainVertex) + mesh->m_waterVertexCount * sizeof(WaterVertex) + mesh->m_waterIndexCount * sizeof(ui16)); // Copy buffer data memcpy(data.data(), verts, VERTS_SIZE * sizeof(TerrainVertex)); ui32 offset = VERTS_SIZE * sizeof(TerrainVertex); // Add water mesh if (m_waterIndexCount) { // Make all vertices relative to the aabb pos for far terrain if (!m_isSpherical) { for (int i = 0; i < m_index; i++) { waterVerts[i].position -= mesh->m_aabbPos; } } // Copy water data memcpy(data.data() + offset, waterVerts, mesh->m_waterVertexCount * sizeof(WaterVertex)); offset += mesh->m_waterVertexCount * sizeof(WaterVertex); memcpy(data.data() + offset, waterIndices, mesh->m_waterIndexCount * sizeof(ui16)); } } void TerrainPatchMesher::uploadMeshData(TerrainPatchMesh* mesh) { // Make VAO glGenVertexArrays(1, &mesh->m_vao); glBindVertexArray(mesh->m_vao); ui32 offset; // Generate the buffers and upload data vg::GpuMemory::createBuffer(mesh->m_vbo); vg::GpuMemory::bindBuffer(mesh->m_vbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(mesh->m_vbo, vg::BufferTarget::ARRAY_BUFFER, VERTS_SIZE * sizeof(TerrainVertex), mesh->m_meshDataBuffer.data()); offset = VERTS_SIZE * sizeof(TerrainVertex); // Reusable IBO vg::GpuMemory::bindBuffer(m_sharedIbo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); // Vertex attribute pointers glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TerrainVertex), offsetptr(TerrainVertex, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TerrainVertex), offsetptr(TerrainVertex, normal)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TerrainVertex), offsetptr(TerrainVertex, color)); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 2, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TerrainVertex), offsetptr(TerrainVertex, temperature)); // Add water mesh if (mesh->m_waterIndexCount) { // Make VAO glGenVertexArrays(1, &mesh->m_wvao); glBindVertexArray(mesh->m_wvao); vg::GpuMemory::createBuffer(mesh->m_wvbo); vg::GpuMemory::bindBuffer(mesh->m_wvbo, vg::BufferTarget::ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(mesh->m_wvbo, vg::BufferTarget::ARRAY_BUFFER, mesh->m_waterVertexCount * sizeof(WaterVertex), mesh->m_meshDataBuffer.data() + offset); offset += mesh->m_waterVertexCount * sizeof(WaterVertex); vg::GpuMemory::createBuffer(mesh->m_wibo); vg::GpuMemory::bindBuffer(mesh->m_wibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER); vg::GpuMemory::uploadBufferData(mesh->m_wibo, vg::BufferTarget::ELEMENT_ARRAY_BUFFER, mesh->m_waterIndexCount * sizeof(ui16), mesh->m_meshDataBuffer.data() + offset); // Vertex attribute pointers glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(WaterVertex), offsetptr(WaterVertex, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(WaterVertex), offsetptr(WaterVertex, tangent)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(WaterVertex), offsetptr(WaterVertex, color)); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(WaterVertex), offsetptr(WaterVertex, depth)); } // Clear the data std::vector().swap(mesh->m_meshDataBuffer); glBindVertexArray(0); } void TerrainPatchMesher::buildSkirts() { const float SKIRT_DEPTH = m_vertWidth * 3.0f; // Top Skirt for (int i = 0; i < PATCH_WIDTH; i++) { auto& v = verts[m_index]; // Copy the vertices from the top edge v = verts[i]; // Extrude downward if (m_isSpherical) { float len = glm::length(v.position) - SKIRT_DEPTH; v.position = glm::normalize(v.position) * len; } else { v.position.y -= SKIRT_DEPTH; } m_index++; } // Left Skirt for (int i = 0; i < PATCH_WIDTH; i++) { auto& v = verts[m_index]; // Copy the vertices from the left edge v = verts[i * PATCH_WIDTH]; // Extrude downward if (m_isSpherical) { float len = glm::length(v.position) - SKIRT_DEPTH; v.position = glm::normalize(v.position) * len; } else { v.position.y -= SKIRT_DEPTH; } m_index++; } // Right Skirt for (int i = 0; i < PATCH_WIDTH; i++) { auto& v = verts[m_index]; // Copy the vertices from the right edge v = verts[i * PATCH_WIDTH + PATCH_WIDTH - 1]; // Extrude downward if (m_isSpherical) { float len = glm::length(v.position) - SKIRT_DEPTH; v.position = glm::normalize(v.position) * len; } else { v.position.y -= SKIRT_DEPTH; } m_index++; } // Bottom Skirt for (int i = 0; i < PATCH_WIDTH; i++) { auto& v = verts[m_index]; // Copy the vertices from the bottom edge v = verts[PATCH_SIZE - PATCH_WIDTH + i]; // Extrude downward if (m_isSpherical) { float len = glm::length(v.position) - SKIRT_DEPTH; v.position = glm::normalize(v.position) * len; } else { v.position.y -= SKIRT_DEPTH; } m_index++; } } void TerrainPatchMesher::addWater(int z, int x, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]) { // Try add all adjacent vertices if needed tryAddWaterVertex(z - 1, x - 1, heightData); tryAddWaterVertex(z - 1, x, heightData); tryAddWaterVertex(z - 1, x + 1, heightData); tryAddWaterVertex(z, x - 1, heightData); tryAddWaterVertex(z, x, heightData); tryAddWaterVertex(z, x + 1, heightData); tryAddWaterVertex(z + 1, x - 1, heightData); tryAddWaterVertex(z + 1, x, heightData); tryAddWaterVertex(z + 1, x + 1, heightData); // Try add quads tryAddWaterQuad(z - 1, x - 1); tryAddWaterQuad(z - 1, x); tryAddWaterQuad(z, x - 1); tryAddWaterQuad(z, x); } void TerrainPatchMesher::tryAddWaterVertex(int z, int x, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]) { // TEMPORARY? Add slight offset so we don't need skirts f32 mvw = m_vertWidth * 1.005f; // const f32 UV_SCALE = 0.04f; if (z < 0 || x < 0 || z >= PATCH_WIDTH || x >= PATCH_WIDTH) return; if (waterIndexGrid[z][x] == 0) { waterIndexGrid[z][x] = m_waterIndex + 1; auto& v = waterVerts[m_waterIndex]; // Set the position based on which face we are on v.position[m_coordMapping.x] = (x * mvw + m_startPos.x) * m_coordMults.x; v.position[m_coordMapping.y] = m_startPos.y; v.position[m_coordMapping.z] = (z * mvw + m_startPos.z) * m_coordMults.y; // Spherify it! // TODO(Ben): Use normal data f32v3 normal; if (m_isSpherical) { normal = glm::normalize(v.position); v.position = normal * m_radius; } else { const i32v3& trueMapping = VoxelSpaceConversions::VOXEL_TO_WORLD[(int)m_cubeFace]; f32v3 tmpPos; tmpPos[trueMapping.x] = v.position.x; tmpPos[trueMapping.y] = m_radius * (f32)VoxelSpaceConversions::FACE_Y_MULTS[(int)m_cubeFace]; tmpPos[trueMapping.z] = v.position.z; normal = glm::normalize(tmpPos); } f32 d = heightData[z + 1][x + 1].height * (f32)M_PER_VOXEL; if (d < 0) { v.depth = -d; } else { v.depth = 0; } v.temperature = heightData[z + 1][x + 1].temperature; // Compute tangent f32v3 tmpPos; tmpPos[m_coordMapping.x] = ((x + 1) * mvw + m_startPos.x) * m_coordMults.x; tmpPos[m_coordMapping.y] = m_startPos.y; tmpPos[m_coordMapping.z] = (z * mvw + m_startPos.z) * m_coordMults.y; tmpPos = glm::normalize(tmpPos) * m_radius; v.tangent = glm::normalize(tmpPos - v.position); // Make sure tangent is orthogonal f32v3 binormal = glm::normalize(glm::cross(glm::normalize(v.position), v.tangent)); v.tangent = glm::normalize(glm::cross(binormal, glm::normalize(v.position))); v.color = m_planetGenData->liquidTint; // TODO(Ben): This is temporary edge debugging stuff const float delta = 100.0f; if (abs(v.position[m_coordMapping.x]) >= m_radius - delta || abs(v.position[m_coordMapping.z]) >= m_radius - delta) { v.color.r = 255; v.color.g = 0; v.color.b = 0; } m_waterIndex++; } } void TerrainPatchMesher::tryAddWaterQuad(int z, int x) { if (z < 0 || x < 0 || z >= PATCH_WIDTH - 1 || x >= PATCH_WIDTH - 1) return; if (!waterQuads[z][x]) { waterQuads[z][x] = true; waterIndices[m_waterIndexCount++] = waterIndexGrid[z][x] - 1; waterIndices[m_waterIndexCount++] = waterIndexGrid[z + 1][x] - 1; waterIndices[m_waterIndexCount++] = waterIndexGrid[z + 1][x + 1] - 1; waterIndices[m_waterIndexCount++] = waterIndexGrid[z + 1][x + 1] - 1; waterIndices[m_waterIndexCount++] = waterIndexGrid[z][x + 1] - 1; waterIndices[m_waterIndexCount++] = waterIndexGrid[z][x] - 1; } } ================================================ FILE: SoA/TerrainPatchMesher.h ================================================ /// /// TerrainPatchMesher.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 3 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Creates terrain patch meshes /// #pragma once #ifndef TerrainPatchMesher_h__ #define TerrainPatchMesher_h__ #include #include "TerrainPatchMesh.h" #include "PlanetHeightData.h" struct PlanetGenData; class TerrainPatchMeshManager; class TerrainPatchMesher { public: /// Generates shared index buffer. static void generateIndices(); static void destroyIndices(); /// Generates mesh using heightmap void generateMeshData(TerrainPatchMesh* mesh, const PlanetGenData* planetGenData, const f32v3& startPos, WorldCubeFace cubeFace, float width, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH], f64v3 positionData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]); static void uploadMeshData(TerrainPatchMesh* mesh); static const int VERTS_SIZE = PATCH_SIZE + PATCH_WIDTH * 4; ///< Number of vertices per patch private: /// Builds the skirts for a patch void buildSkirts(); /// Adds water at a given point /// @param z: Z position /// @Param x: X position /// @param heightData: The heightmap data void addWater(int z, int x, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]); /// Tries to add a water vertex at a given spot if one doesn't exist /// @param z: Z position /// @param x: X position /// @param heightData: The heightmap data void tryAddWaterVertex(int z, int x, PlanetHeightData heightData[PADDED_PATCH_WIDTH][PADDED_PATCH_WIDTH]); /// Tries to add a quad at a given spot if one doesnt exist /// @param z: Z position /// @param x: X position void tryAddWaterQuad(int z, int x); static VGIndexBuffer m_sharedIbo; ///< Reusable CCW IBO // PATCH_WIDTH * 4 is for skirts TerrainVertex verts[VERTS_SIZE]; ///< Vertices for terrain mesh WaterVertex waterVerts[VERTS_SIZE]; ///< Vertices for water mesh ui16 waterIndexGrid[PATCH_WIDTH][PATCH_WIDTH]; ///< Caches water indices for reuse ui16 waterIndices[PATCH_INDICES]; ///< Buffer of indices to upload bool waterQuads[PATCH_WIDTH - 1][PATCH_WIDTH - 1]; ///< True when a quad is present at a spot const PlanetGenData* m_planetGenData = nullptr; ///< Planetary data // TerrainPatchMeshManager* m_meshManager = nullptr; ///< Manages the patch meshes /// Meshing helper vars int m_index; int m_waterIndex; int m_waterIndexCount; f32 m_vertWidth; f32 m_radius; i32v3 m_coordMapping; f32v3 m_startPos; f32v2 m_coordMults; bool m_isSpherical; WorldCubeFace m_cubeFace; }; #endif // TerrainPatchMesher_h__ ================================================ FILE: SoA/TestBiomeScreen.cpp ================================================ #include "stdafx.h" #include "TestBiomeScreen.h" #include #include #include "App.h" #include "ChunkRenderer.h" #include "DevConsole.h" #include "InputMapper.h" #include "Inputs.h" #include "LoadTaskBlockData.h" #include "RenderUtils.h" #include "ShaderLoader.h" #include "SoaEngine.h" #include "SoAState.h" #ifdef DEBUG #define HORIZONTAL_CHUNKS 26 #define VERTICAL_CHUNKS 4 #else #define HORIZONTAL_CHUNKS 26 #define VERTICAL_CHUNKS 20 #endif TestBiomeScreen::TestBiomeScreen(const App* app, CommonState* state) : IAppScreen(app), m_commonState(state), m_soaState(m_commonState->state), m_blockArrayRecycler(1000) { } i32 TestBiomeScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestBiomeScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestBiomeScreen::build() { } void TestBiomeScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestBiomeScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // Init spritebatch and font m_sb.init(); m_font.init("Fonts/orbitron_bold-webfont.ttf", 32); // Init game state SoaEngine::initState(m_commonState->state); // Init access m_accessor.init(&m_allocator); // Init renderer m_renderer.init(); { // Init post processing Array attachments; vg::GBufferAttachment att[2]; // Color att[0].format = vg::TextureInternalFormat::RGBA16F; att[0].pixelFormat = vg::TextureFormat::RGBA; att[0].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[0].number = 1; // Normals att[1].format = vg::TextureInternalFormat::RGBA16F; att[1].pixelFormat = vg::TextureFormat::RGBA; att[1].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[1].number = 2; m_hdrTarget.setSize(m_commonState->window->getWidth(), m_commonState->window->getHeight()); m_hdrTarget.init(Array(att, 2), vg::TextureInternalFormat::RGBA8).initDepth(); // Swapchain m_swapChain.init(m_commonState->window->getWidth(), m_commonState->window->getHeight(), vg::TextureInternalFormat::RGBA16F); // Init the FullQuadVBO m_commonState->quad.init(); // SSAO m_ssaoStage.init(m_commonState->window, m_commonState->loadContext); m_ssaoStage.load(m_commonState->loadContext); m_ssaoStage.hook(&m_commonState->quad, m_commonState->window->getWidth(), m_commonState->window->getHeight()); // HDR m_hdrStage.init(m_commonState->window, m_commonState->loadContext); m_hdrStage.load(m_commonState->loadContext); m_hdrStage.hook(&m_commonState->quad); } // Load test planet PlanetGenLoader planetLoader; m_iom.setSearchDirectory("StarSystems/Trinity/"); planetLoader.init(&m_iom); m_genData = planetLoader.loadPlanetGenData("Moons/Aldrin/terrain_gen.yml"); if (m_genData->terrainColorPixels.data) { m_soaState->clientState.blockTextures->setColorMap("biome", &m_genData->terrainColorPixels); } // Load blocks LoadTaskBlockData blockLoader(&m_soaState->blocks, &m_soaState->clientState.blockTextureLoader, &m_commonState->loadContext); blockLoader.load(); // Uploads all the needed textures m_soaState->clientState.blockTextures->update(); m_genData->radius = 4500.0; // Set blocks SoaEngine::initVoxelGen(m_genData, m_soaState->blocks); m_chunkGenerator.init(m_genData); initHeightData(); initChunks(); printf("Generating Meshes...\n"); // Create all chunk meshes m_mesher.init(&m_soaState->blocks); for (auto& cv : m_chunks) { cv.chunkMesh = m_mesher.easyCreateChunkMesh(cv.chunk, MeshTaskType::DEFAULT); } { // Init the camera m_camera.init(m_commonState->window->getAspectRatio()); m_camera.setPosition(f64v3(16.0, 17.0, 33.0)); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setRight(f32v3(1.0f, 0.0f, 0.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); } initInput(); // Initialize dev console vui::GameWindow* window = m_commonState->window; m_devConsoleView.init(&DevConsole::getInstance(), 5, f32v2(20.0f, window->getHeight() - 400.0f), window->getWidth() - 40.0f); // Set GL state glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearDepth(1.0); printf("Done."); } void TestBiomeScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { for (auto& cv : m_chunks) { m_mesher.freeChunkMesh(cv.chunkMesh); } m_hdrTarget.dispose(); m_swapChain.dispose(); m_devConsoleView.dispose(); } void TestBiomeScreen::update(const vui::GameTime& gameTime) { f32 speed = 10.0f; if (m_movingFast) speed *= 5.0f; if (m_movingForward) { f32v3 offset = m_camera.getDirection() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingBack) { f32v3 offset = m_camera.getDirection() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingLeft) { f32v3 offset = m_camera.getRight() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingRight) { f32v3 offset = m_camera.getRight() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingUp) { f32v3 offset = f32v3(0, 1, 0) * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingDown) { f32v3 offset = f32v3(0, 1, 0) * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } m_camera.update(); } void TestBiomeScreen::draw(const vui::GameTime& gameTime VORB_UNUSED) { // Bind the FBO m_hdrTarget.useGeometry(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Opaque m_renderer.beginOpaque(m_soaState->clientState.blockTextures->getAtlasTexture(), f32v3(0.0f, 0.0f, -1.0f), f32v3(1.0f), f32v3(0.3f)); for (auto& vc : m_chunks) { if (m_camera.getFrustum().sphereInFrustum(f32v3(vc.chunkMesh->position + f64v3(CHUNK_WIDTH / 2) - m_camera.getPosition()), CHUNK_DIAGONAL_LENGTH)) { vc.inFrustum = true; m_renderer.drawOpaque(vc.chunkMesh, m_camera.getPosition(), m_camera.getViewProjectionMatrix()); } else { vc.inFrustum = false; } } // Cutout m_renderer.beginCutout(m_soaState->clientState.blockTextures->getAtlasTexture(), f32v3(0.0f, 0.0f, -1.0f), f32v3(1.0f), f32v3(0.3f)); for (auto& vc : m_chunks) { if (vc.inFrustum) { m_renderer.drawCutout(vc.chunkMesh, m_camera.getPosition(), m_camera.getViewProjectionMatrix()); } } m_renderer.end(); if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Post processing m_swapChain.reset(0, m_hdrTarget.getGeometryID(), m_hdrTarget.getGeometryTexture(0), soaOptions.get(OPT_MSAA).value.i > 0, false); // Render SSAO m_ssaoStage.set(m_hdrTarget.getDepthTexture(), m_hdrTarget.getGeometryTexture(1), m_hdrTarget.getGeometryTexture(0), m_swapChain.getCurrent().getID()); m_ssaoStage.render(&m_camera); m_swapChain.swap(); m_swapChain.use(0, false); // Draw to backbuffer for the last effect // TODO(Ben): Do we really need to clear depth here... glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_hdrTarget.getDepthTexture()); m_hdrStage.render(); // Draw UI char buf[256]; sprintf(buf, "FPS: %.1f", m_app->getFps()); m_sb.begin(); m_sb.drawString(&m_font, buf, f32v2(30.0f), f32v2(1.0f), color::White); m_sb.end(); m_sb.render(f32v2(m_commonState->window->getViewportDims())); vg::DepthState::FULL.set(); // Have to restore depth // Draw dev console m_devConsoleView.update(0.01f); m_devConsoleView.render(m_game->getWindow().getViewportDims()); } void TestBiomeScreen::initHeightData() { printf("Generating height data...\n"); m_heightData.resize(HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS); // Init height data m_heightGenerator.init(m_genData); for (int z = 0; z < HORIZONTAL_CHUNKS; z++) { for (int x = 0; x < HORIZONTAL_CHUNKS; x++) { auto& hd = m_heightData[z * HORIZONTAL_CHUNKS + x]; for (int i = 0; i < CHUNK_WIDTH; i++) { for (int j = 0; j < CHUNK_WIDTH; j++) { VoxelPosition2D pos; pos.pos.x = x * CHUNK_WIDTH + j + 3000; pos.pos.y = z * CHUNK_WIDTH + i + 3000; PlanetHeightData& data = hd.heightData[i * CHUNK_WIDTH + j]; m_heightGenerator.generateHeightData(data, pos); data.temperature = 128; data.humidity = 128; } } } } // Get center height f32 cHeight = m_heightData[HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS / 2].heightData[CHUNK_LAYER / 2].height; // Center the heightmap for (auto& hd : m_heightData) { for (int i = 0; i < CHUNK_LAYER; i++) { hd.heightData[i].height -= cHeight; } } } void TestBiomeScreen::initChunks() { printf("Generating chunks...\n"); m_chunks.resize(HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS * VERTICAL_CHUNKS); // Generate chunk data for (size_t i = 0; i < m_chunks.size(); i++) { ChunkPosition3D pos; i32v3 gridPosition; gridPosition.x = i % HORIZONTAL_CHUNKS; gridPosition.y = i / (HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS); gridPosition.z = (i % (HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS)) / HORIZONTAL_CHUNKS; // Center position about origin pos.pos.x = gridPosition.x - HORIZONTAL_CHUNKS / 2; pos.pos.y = gridPosition.y - VERTICAL_CHUNKS / 4; pos.pos.z = gridPosition.z - HORIZONTAL_CHUNKS / 2; // Init parameters ChunkID id(pos.x, pos.y, pos.z); m_chunks[i].chunk = m_accessor.acquire(id); m_chunks[i].chunk->init(WorldCubeFace::FACE_TOP); m_chunks[i].gridPosition = gridPosition; m_chunks[i].chunk->gridData = &m_heightData[i % (HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS)]; // Generate the chunk m_chunkGenerator.generateChunk(m_chunks[i].chunk, m_chunks[i].chunk->gridData->heightData); // Decompress to flat array m_chunks[i].chunk->blocks.setArrayRecycler(&m_blockArrayRecycler); m_chunks[i].chunk->blocks.changeState(vvox::VoxelStorageState::FLAT_ARRAY, m_chunks[i].chunk->dataMutex); } // Generate flora std::vector lNodes; std::vector wNodes; // TODO(Ben): I know this is ugly PreciseTimer t1; t1.start(); for (size_t i = 0; i < m_chunks.size(); i++) { Chunk* chunk = m_chunks[i].chunk; m_floraGenerator.generateChunkFlora(chunk, m_heightData[i % (HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS)].heightData, lNodes, wNodes); for (auto& node : wNodes) { i32v3 gridPos = m_chunks[i].gridPosition; gridPos.x += FloraGenerator::getChunkXOffset(node.chunkOffset); gridPos.y += FloraGenerator::getChunkYOffset(node.chunkOffset); gridPos.z += FloraGenerator::getChunkZOffset(node.chunkOffset); if (gridPos.x >= 0 && gridPos.y >= 0 && gridPos.z >= 0 && gridPos.x < HORIZONTAL_CHUNKS && gridPos.y < VERTICAL_CHUNKS && gridPos.z < HORIZONTAL_CHUNKS) { Chunk* chunk = m_chunks[gridPos.x + gridPos.y * HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS + gridPos.z * HORIZONTAL_CHUNKS].chunk; chunk->blocks.set(node.blockIndex, node.blockID); } } for (auto& node : lNodes) { i32v3 gridPos = m_chunks[i].gridPosition; gridPos.x += FloraGenerator::getChunkXOffset(node.chunkOffset); gridPos.y += FloraGenerator::getChunkYOffset(node.chunkOffset); gridPos.z += FloraGenerator::getChunkZOffset(node.chunkOffset); if (gridPos.x >= 0 && gridPos.y >= 0 && gridPos.z >= 0 && gridPos.x < HORIZONTAL_CHUNKS && gridPos.y < VERTICAL_CHUNKS && gridPos.z < HORIZONTAL_CHUNKS) { Chunk* chunk = m_chunks[gridPos.x + gridPos.y * HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS + gridPos.z * HORIZONTAL_CHUNKS].chunk; if (chunk->blocks.get(node.blockIndex) == 0) { chunk->blocks.set(node.blockIndex, node.blockID); } } } std::vector().swap(chunk->floraToGenerate); lNodes.clear(); wNodes.clear(); } printf("Tree Gen Time %lf\n", t1.stop()); #define GET_INDEX(x, y, z) ((x) + (y) * HORIZONTAL_CHUNKS * HORIZONTAL_CHUNKS + (z) * HORIZONTAL_CHUNKS) // Set neighbor pointers for (int y = 0; y < VERTICAL_CHUNKS; y++) { for (int z = 0; z < HORIZONTAL_CHUNKS; z++) { for (int x = 0; x < HORIZONTAL_CHUNKS; x++) { Chunk* chunk = m_chunks[GET_INDEX(x, y, z)].chunk; // TODO(Ben): Release these too. if (x > 0) { chunk->neighbor.left = m_chunks[GET_INDEX(x - 1, y, z)].chunk.acquire(); } if (x < HORIZONTAL_CHUNKS - 1) { chunk->neighbor.right = m_chunks[GET_INDEX(x + 1, y, z)].chunk.acquire(); } if (y > 0) { chunk->neighbor.bottom = m_chunks[GET_INDEX(x, y - 1, z)].chunk.acquire(); } if (y < VERTICAL_CHUNKS - 1) { chunk->neighbor.top = m_chunks[GET_INDEX(x, y + 1, z)].chunk.acquire(); } if (z > 0) { chunk->neighbor.back = m_chunks[GET_INDEX(x, y, z - 1)].chunk.acquire(); } if (z < HORIZONTAL_CHUNKS - 1) { chunk->neighbor.front = m_chunks[GET_INDEX(x, y, z + 1)].chunk.acquire(); } } } } } void TestBiomeScreen::initInput() { m_inputMapper = new InputMapper; initInputs(m_inputMapper); m_mouseButtons[0] = false; m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { if (m_mouseButtons[0]) { m_camera.rotateFromMouseAbsoluteUp(-e.dx, -e.dy, 0.01f, true); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_mouseButtons[0] = !m_mouseButtons[0]; if (m_mouseButtons[0]) { SDL_SetRelativeMouseMode(SDL_TRUE); } else { SDL_SetRelativeMouseMode(SDL_FALSE); } }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { PlanetGenLoader planetLoader; switch (e.keyCode) { case VKEY_W: m_movingForward = true; break; case VKEY_S: m_movingBack = true; break; case VKEY_A: m_movingLeft = true; break; case VKEY_D: m_movingRight = true; break; case VKEY_SPACE: m_movingUp = true; break; case VKEY_LSHIFT: m_movingFast = true; break; case VKEY_M: m_wireFrame = !m_wireFrame; break; case VKEY_LEFT: /* if (m_activeChunk == 0) { m_activeChunk = m_chunks.size() - 1; } else { m_activeChunk--; }*/ break; case VKEY_RIGHT: /* m_activeChunk++; if (m_activeChunk >= (int)m_chunks.size()) m_activeChunk = 0;*/ break; case VKEY_F10: // Reload meshes // TODO(Ben): Destroy meshes for (auto& cv : m_chunks) { m_mesher.freeChunkMesh(cv.chunkMesh); cv.chunkMesh = m_mesher.easyCreateChunkMesh(cv.chunk, MeshTaskType::DEFAULT); } break; case VKEY_F11: // Reload shaders m_renderer.dispose(); m_renderer.init(); m_ssaoStage.reloadShaders(); break; case VKEY_F12: // Reload world delete m_genData; planetLoader.init(&m_iom); m_genData = planetLoader.loadPlanetGenData("Planets/Aldrin/terrain_gen.yml"); m_genData->radius = 4500.0; // Set blocks SoaEngine::initVoxelGen(m_genData, m_soaState->blocks); m_chunkGenerator.init(m_genData); for (auto& cv : m_chunks) { m_mesher.freeChunkMesh(cv.chunkMesh); cv.chunk.release(); } initHeightData(); initChunks(); printf("Generating Meshes...\n"); // Create all chunk meshes m_mesher.init(&m_soaState->blocks); for (auto& cv : m_chunks) { cv.chunkMesh = m_mesher.easyCreateChunkMesh(cv.chunk, MeshTaskType::DEFAULT); } break; } }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch (e.keyCode) { case VKEY_W: m_movingForward = false; break; case VKEY_S: m_movingBack = false; break; case VKEY_A: m_movingLeft = false; break; case VKEY_D: m_movingRight = false; break; case VKEY_SPACE: m_movingUp = false; break; case VKEY_LSHIFT: m_movingFast = false; break; } }); // Dev console m_hooks.addAutoHook(m_inputMapper->get(INPUT_DEV_CONSOLE).downEvent, [](Sender s VORB_MAYBE_UNUSED, ui32 a VORB_MAYBE_UNUSED) { DevConsole::getInstance().toggleFocus(); }); m_inputMapper->startInput(); } ================================================ FILE: SoA/TestBiomeScreen.h ================================================ #include #include #include #include #include #include #include #include #include #include "BlockPack.h" #include "Camera.h" #include "Chunk.h" #include "ChunkAllocator.h" #include "ChunkMesher.h" #include "ChunkRenderer.h" #include "CommonState.h" #include "DevConsoleView.h" #include "FloraGenerator.h" #include "HdrRenderStage.h" #include "SsaoRenderStage.h" #include "SphericalHeightmapGenerator.h" class InputMapper; class TestBiomeScreen : public vui::IAppScreen < App > { public: TestBiomeScreen(const App* app, CommonState* state); /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ i32 getNextScreen() const override; i32 getPreviousScreen() const override; void build() override; void destroy(const vui::GameTime& gameTime) override; void onEntry(const vui::GameTime& gameTime) override; void onExit(const vui::GameTime& gameTime) override; void update(const vui::GameTime& gameTime) override; void draw(const vui::GameTime& gameTime) override; private: void initHeightData(); void initChunks(); void initInput(); struct ViewableChunk { ChunkHandle chunk; ChunkMesh* chunkMesh; i32v3 gridPosition; bool inFrustum; }; AutoDelegatePool m_hooks; ///< Input hooks reservoir Camera m_camera; BlockPack m_blocks; ///< Block data CommonState* m_commonState; SoaState* m_soaState; ChunkRenderer m_renderer; ChunkMesher m_mesher; ProceduralChunkGenerator m_chunkGenerator; SphericalHeightmapGenerator m_heightGenerator; FloraGenerator m_floraGenerator; PlanetGenData* m_genData = nullptr; vio::IOManager m_iom; PagedChunkAllocator m_allocator; ChunkAccessor m_accessor; vg::SpriteBatch m_sb; vg::SpriteFont m_font; std::vector m_chunks; std::vector m_heightData; vcore::FixedSizeArrayRecycler m_blockArrayRecycler; vg::GBuffer m_hdrTarget; ///< Framebuffer needed for the HDR rendering vg::RTSwapChain<2> m_swapChain; ///< Swap chain of framebuffers used for post-processing SSAORenderStage m_ssaoStage; HdrRenderStage m_hdrStage; DevConsoleView m_devConsoleView; InputMapper* m_inputMapper = nullptr; bool m_wireFrame = false; bool m_mouseButtons[2]; bool m_movingForward = false; bool m_movingBack = false; bool m_movingLeft = false; bool m_movingRight = false; bool m_movingUp = false; bool m_movingDown = false; bool m_movingFast = false; }; ================================================ FILE: SoA/TestBlockViewScreen.cpp ================================================ #include "stdafx.h" #include "TestBlockViewScreen.h" #include #include #include #include #include #include #include #include "BlockLoader.h" #include #include #ifdef _MSC_VER #pragma region Simple shader code #endif// _MSC_VER const cString SRC_VERT_BLOCK = R"( uniform mat4 unWVP; in vec4 vPosition; in vec3 vColor; in vec3 vUV; out vec3 fUV; out vec3 fColor; void main() { fUV = vUV; fColor = vColor; gl_Position = unWVP * vPosition; } )"; const cString SRC_FRAG_BLOCK = R"( in vec3 fUV; in vec3 fColor; out vec4 pColor; void main() { vec3 ff = fract(fUV); float f1 = min(ff.x, ff.y); float f2 = max(ff.x, ff.y); float f = 1.0 - max(1.0 - f1, f2); pColor = vec4(fColor * f , 1); } )"; #ifdef _MSC_VER #pragma endregion #endif// _MSC_VER struct VertexPosColor { public: f32v3 pos; f32v2 uv; color3 color; }; f32v3 BLOCK_MODEL[24] = { f32v3(0, 1, 0), f32v3(0, 1, 1), f32v3(0, 0, 0), f32v3(0, 0, 1), f32v3(1, 1, 1), f32v3(1, 1, 0), f32v3(1, 0, 1), f32v3(1, 0, 0), f32v3(0, 0, 1), f32v3(1, 0, 1), f32v3(0, 0, 0), f32v3(1, 0, 0), f32v3(0, 1, 0), f32v3(1, 1, 0), f32v3(0, 1, 1), f32v3(1, 1, 1), f32v3(1, 1, 0), f32v3(0, 1, 0), f32v3(1, 0, 0), f32v3(0, 0, 0), f32v3(0, 1, 1), f32v3(1, 1, 1), f32v3(0, 0, 1), f32v3(1, 0, 1) }; f32v2 BLOCK_UVS[4] = { f32v2(0, 1), f32v2(1, 1), f32v2(0, 0), f32v2(1, 0) }; const ui32 TEST_CHUNK_SIZE = 34; class APICullSimple { public: APICullSimple(const ui16* data) : m_data(data) { // Empty } vvox::meshalg::VoxelFaces occludes(const ui16& v1, const ui16& v2, const vvox::Axis& axis VORB_MAYBE_UNUSED) const { vvox::meshalg::VoxelFaces f = {}; if (v1 != 0 && v2 == 0) f.block1Face = true; else if (v2 != 0 && v1 == 0) f.block2Face = true; return f; } void result(const vvox::meshalg::VoxelQuad& quad) { VertexPosColor v; ColorRGBA8 c; switch (m_data[quad.startIndex]) { case 1: c = ColorRGBA8((i32)(quad.voxelPosition.x / 3.0f * 255), (i32)(quad.voxelPosition.y / 3.0f * 255), (i32)(quad.voxelPosition.z / 3.0f * 255)); v.color = c.color.rgb; break; default: v.color = color::Blue.color.rgb; break; } f32v3 off(quad.voxelPosition); f32v3 scale; switch (quad.direction) { case vvox::Cardinal::X_NEG: case vvox::Cardinal::X_POS: scale.x = 1; scale.z = (f32)quad.size.x; scale.y = (f32)quad.size.y; break; case vvox::Cardinal::Y_NEG: case vvox::Cardinal::Y_POS: scale.y = 1; scale.x = (f32)quad.size.x; scale.z = (f32)quad.size.y; break; case vvox::Cardinal::Z_NEG: case vvox::Cardinal::Z_POS: scale.z = 1; scale.x = (f32)quad.size.x; scale.y = (f32)quad.size.y; break; default: break; } for (size_t i = 0; i < 4; i++) { v.uv = BLOCK_UVS[i]; v.pos = off + BLOCK_MODEL[(size_t)quad.direction * 4 + i] * scale; vertices.emplace_back(v); } } std::vector vertices; private: const ui16* m_data; }; i32 TestBlockView::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestBlockView::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestBlockView::build() { // Empty } void TestBlockView::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestBlockView::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_hooks.addAutoHook(m_blocks.onBlockAddition, [&] (Sender s VORB_MAYBE_UNUSED, ui16 id) { printf("Loaded Block: %s = %d\n", m_blocks[id].name.c_str(), id); }); m_hooks.addAutoHook(vui::InputDispatcher::window.onFile, [&] (Sender s VORB_MAYBE_UNUSED, const vui::WindowFileEvent& e) { loadBlocks(e.file); }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [&] (Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { if (m_movingCamera) { m_mRotation = glm::rotate(f32m4(), 1.2f * e.dx, f32v3(0.0f, 1.0f, 0.0f)) * m_mRotation; m_mRotation = glm::rotate(f32m4(), 1.2f * e.dy, f32v3(1.0f, 0.0f, 0.0f)) * m_mRotation; } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&] (Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::MIDDLE) m_movingCamera = true; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&] (Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::MIDDLE) m_movingCamera = false; }); m_program.init(); m_program.addShader(vg::ShaderType::VERTEX_SHADER, SRC_VERT_BLOCK); m_program.addShader(vg::ShaderType::FRAGMENT_SHADER, SRC_FRAG_BLOCK); m_program.link(); m_program.initAttributes(); m_program.initUniforms(); genBlockMesh(); m_mRotation = f32m4(1.0); m_movingCamera = false; // Set clear state glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0); } void TestBlockView::onExit(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestBlockView::update(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestBlockView::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_program.use(); f32 tCenter = (f32)TEST_CHUNK_SIZE * -0.5f; f32m4 mWVP = glm::perspectiveFov(90.0f, 800.0f, 600.0f, 0.1f, 1000.0f) * glm::lookAt(f32v3(0, 0, TEST_CHUNK_SIZE), f32v3(0, 0, 0), f32v3(0, 1, 0)) * m_mRotation * glm::translate(f32v3(tCenter, tCenter, tCenter)); vg::DepthState::FULL.set(); vg::RasterizerState::CULL_CLOCKWISE.set(); m_program.use(); m_program.enableVertexAttribArrays(); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, false, (f32*)&mWVP[0][0]); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, false, sizeof(VertexPosColor), offsetptr(VertexPosColor, pos)); glVertexAttribPointer(m_program.getAttribute("vUV"), 2, GL_FLOAT, false, sizeof(VertexPosColor), offsetptr(VertexPosColor, uv)); glVertexAttribPointer(m_program.getAttribute("vColor"), 3, GL_UNSIGNED_BYTE, true, sizeof(VertexPosColor), offsetptr(VertexPosColor, color)); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); glDrawElements(GL_TRIANGLES, m_indCount, GL_UNSIGNED_INT, nullptr); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); m_program.disableVertexAttribArrays(); vg::GLProgram::unuse(); } void TestBlockView::loadBlocks(const cString file) { vio::IOManager iom; BlockLoader::load(iom, file, &m_blocks); } void TestBlockView::genBlockMesh() { size_t vc = TEST_CHUNK_SIZE * TEST_CHUNK_SIZE * TEST_CHUNK_SIZE; ui16* data = new ui16[vc](); Random r; r.seed(10); for (ui32 z = 0; z < TEST_CHUNK_SIZE; z++) { for (ui32 x = 0; x < TEST_CHUNK_SIZE; x++) { ui32 h = (ui32)(r.genMH() * 4) + 15; for (ui32 y = 0; y < h; y++) { size_t vi = (y * TEST_CHUNK_SIZE + z) * TEST_CHUNK_SIZE + x; data[vi] = 1; } } } APICullSimple api(data); api.vertices.reserve(10000); vvox::meshalg::createCulled(data, ui32v3(TEST_CHUNK_SIZE), &api); delete[] data; glGenBuffers(1, &m_verts); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glBufferData(GL_ARRAY_BUFFER, api.vertices.size() * sizeof(VertexPosColor), api.vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); m_indCount = (api.vertices.size()) / 4 * 6; printf("Tris: %d\n", m_indCount / 3); ui32* inds = vvox::meshalg::generateQuadIndices(api.vertices.size() / 4); glGenBuffers(1, &m_inds); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indCount * sizeof(ui32), inds, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); delete[] inds; } ================================================ FILE: SoA/TestBlockViewScreen.h ================================================ /// /// TestBlockViewScreen.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 23 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Preview a block /// #pragma once #ifndef TestBlockViewScreen_h__ #define TestBlockViewScreen_h__ #include #include #include #include "BlockPack.h" class TestBlockView : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: /// Loads a file of block data /// @param file: File containing block data void loadBlocks(const cString file); void genBlockMesh(); BlockPack m_blocks; ///< Block data AutoDelegatePool m_hooks; ///< Input hooks reservoir VGVertexBuffer m_verts; VGVertexBuffer m_inds; ui32 m_indCount; vg::GLProgram m_program; f32m4 m_mRotation; bool m_movingCamera; }; #endif // TestBlockViewScreen_h__ ================================================ FILE: SoA/TestConnectedTextureScreen.cpp ================================================ #include "stdafx.h" #include "TestConnectedTextureScreen.h" #include #include #include "SoAState.h" #include "SoaEngine.h" #include "LoadTaskBlockData.h" #include "ChunkRenderer.h" #include "ChunkMeshTask.h" TestConnectedTextureScreen::TestConnectedTextureScreen(const App* app, CommonState* state) : IAppScreen(app), m_commonState(state), m_soaState(m_commonState->state) { } i32 TestConnectedTextureScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestConnectedTextureScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestConnectedTextureScreen::build() { } void TestConnectedTextureScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestConnectedTextureScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if(e.keyCode==VKEY_ESCAPE) { exit(0); } }); // Init spritebatch and font m_sb.init(); m_font.init("Fonts/orbitron_bold-webfont.ttf", 32); // Init game state SoaEngine::initState(m_commonState->state); // Init renderer m_renderer.init(); { // Init post processing Array attachments; vg::GBufferAttachment att[2]; // Color att[0].format = vg::TextureInternalFormat::RGBA16F; att[0].pixelFormat = vg::TextureFormat::RGBA; att[0].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[0].number = 1; // Normals att[1].format = vg::TextureInternalFormat::RGBA16F; att[1].pixelFormat = vg::TextureFormat::RGBA; att[1].pixelType = vg::TexturePixelType::UNSIGNED_BYTE; att[1].number = 2; m_hdrTarget.setSize(m_commonState->window->getWidth(), m_commonState->window->getHeight()); m_hdrTarget.init(Array(att, 2), vg::TextureInternalFormat::RGBA8).initDepth(); // Swapchain m_swapChain.init(m_commonState->window->getWidth(), m_commonState->window->getHeight(), vg::TextureInternalFormat::RGBA16F); // Init the FullQuadVBO m_commonState->quad.init(); // SSAO m_ssaoStage.init(m_commonState->window, m_commonState->loadContext); m_ssaoStage.load(m_commonState->loadContext); m_ssaoStage.hook(&m_commonState->quad, m_commonState->window->getWidth(), m_commonState->window->getHeight()); // HDR m_hdrStage.init(m_commonState->window, m_commonState->loadContext); m_hdrStage.load(m_commonState->loadContext); m_hdrStage.hook(&m_commonState->quad); } // Load blocks LoadTaskBlockData blockLoader(&m_soaState->blocks, &m_soaState->clientState.blockTextureLoader, &m_commonState->loadContext); blockLoader.load(); // Uploads all the needed textures m_soaState->clientState.blockTextures->update(); initChunks(); // Create all chunk meshes m_mesher.init(&m_soaState->blocks); for (auto& cv : m_chunks) { cv.chunkMesh = m_mesher.easyCreateChunkMesh(cv.chunk, MeshTaskType::DEFAULT); } { // Init the camera m_camera.init(m_commonState->window->getAspectRatio()); m_camera.setPosition(f64v3(16.0, 17.0, 33.0)); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setRight(f32v3(1.0f, 0.0f, 0.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); } initInput(); // Set GL state glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearDepth(1.0); } void TestConnectedTextureScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { for (auto& cv : m_chunks) { m_mesher.freeChunkMesh(cv.chunkMesh); } m_hdrTarget.dispose(); m_swapChain.dispose(); } void TestConnectedTextureScreen::update(const vui::GameTime& gameTime) { f32 speed = 5.0f; if (m_movingFast) speed *= 5.0f; if (m_movingForward) { f32v3 offset = m_camera.getDirection() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingBack) { f32v3 offset = m_camera.getDirection() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingLeft) { f32v3 offset = m_camera.getRight() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingRight) { f32v3 offset = m_camera.getRight() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingUp) { f32v3 offset = f32v3(0, 1, 0) * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if (m_movingDown) { f32v3 offset = f32v3(0, 1, 0) * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } m_camera.update(); } void TestConnectedTextureScreen::draw(const vui::GameTime& gameTime VORB_UNUSED) { // Bind the FBO m_hdrTarget.useGeometry(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); m_renderer.beginOpaque(m_soaState->clientState.blockTextures->getAtlasTexture(), f32v3(0.0f, 0.0f, -1.0f), f32v3(1.0f), f32v3(0.3f)); m_renderer.drawOpaque(m_chunks[m_activeChunk].chunkMesh, m_camera.getPosition(), m_camera.getViewProjectionMatrix()); m_renderer.end(); if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Post processing m_swapChain.reset(0, m_hdrTarget.getGeometryID(), m_hdrTarget.getGeometryTexture(0), soaOptions.get(OPT_MSAA).value.i > 0, false); // Render SSAO m_ssaoStage.set(m_hdrTarget.getDepthTexture(), m_hdrTarget.getGeometryTexture(1), m_hdrTarget.getGeometryTexture(0), m_swapChain.getCurrent().getID()); m_ssaoStage.render(&m_camera); m_swapChain.swap(); m_swapChain.use(0, false); // Draw to backbuffer for the last effect // TODO(Ben): Do we really need to clear depth here... glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_hdrTarget.getDepthTexture()); m_hdrStage.render(); // Draw UI m_sb.begin(); m_sb.drawString(&m_font, (std::to_string(m_activeChunk) + ". " + m_chunks[m_activeChunk].name).c_str(), f32v2(30.0f), f32v2(1.0f), color::White); m_sb.end(); m_sb.render(f32v2(m_commonState->window->getViewportDims())); vg::DepthState::FULL.set(); // Have to restore depth } void TestConnectedTextureScreen::initChunks() { ui16 grass = m_soaState->blocks.getBlockIndex("grass"); ui16 dirt = m_soaState->blocks.getBlockIndex("dirt"); // TODO(Ben): Allow users to pick block { // Grass 1 Chunk* chunk = addChunk("Grass 1"); chunk->setBlock(15, 16, 16, grass); chunk->setBlock(16, 16, 16, grass); chunk->setBlock(17, 16, 16, grass); chunk->setBlock(15, 15, 17, grass); chunk->setBlock(16, 15, 17, grass); chunk->setBlock(17, 15, 17, grass); chunk->setBlock(15, 15, 18, grass); chunk->setBlock(16, 14, 18, grass); chunk->setBlock(17, 14, 18, grass); chunk->setBlock(15, 13, 19, grass); chunk->setBlock(16, 13, 19, grass); chunk->setBlock(17, 14, 19, grass); } { // Hourglass Chunk* chunk = addChunk("Hourglass"); for (int y = 0; y < HALF_CHUNK_WIDTH; y++) { for (int z = y; z < CHUNK_WIDTH - y; z++) { for (int x = y; x < CHUNK_WIDTH - y; x++) { chunk->setBlock(x, y, z, grass); chunk->setBlock(x, CHUNK_WIDTH - y - 1, z, grass); } } } for (int y = 0; y < CHUNK_WIDTH; y++) { chunk->setBlock(0, y, 0, dirt); chunk->setBlock(CHUNK_WIDTH_M1, y, CHUNK_WIDTH_M1, dirt); } } { // Flat Chunk* chunk = addChunk("Flat"); for (int i = 0; i < CHUNK_LAYER; i++) { chunk->blocks.set(CHUNK_LAYER * 15 + i, grass); } } } void TestConnectedTextureScreen::initInput() { m_mouseButtons[0] = false; m_mouseButtons[1] = false; m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { if (m_mouseButtons[0]) { m_camera.rotateFromMouse((f32)-e.dx, (f32)-e.dy, 0.1f); } if (m_mouseButtons[1]) { m_camera.rollFromMouse((f32)e.dx, 0.1f); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_mouseButtons[0] = true; if (e.button == vui::MouseButton::RIGHT) m_mouseButtons[1] = true; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_mouseButtons[0] = false; if (e.button == vui::MouseButton::RIGHT) m_mouseButtons[1] = false; }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch (e.keyCode) { case VKEY_W: m_movingForward = true; break; case VKEY_S: m_movingBack = true; break; case VKEY_A: m_movingLeft = true; break; case VKEY_D: m_movingRight = true; break; case VKEY_SPACE: m_movingUp = true; break; case VKEY_LSHIFT: m_movingFast = true; break; case VKEY_M: m_wireFrame = !m_wireFrame; break; case VKEY_LEFT: if (m_activeChunk == 0) { m_activeChunk = m_chunks.size() - 1; } else { m_activeChunk--; } break; case VKEY_RIGHT: m_activeChunk++; if (m_activeChunk >= (int)m_chunks.size()) m_activeChunk = 0; break; case VKEY_F10: // Reload meshes // TODO(Ben): Destroy meshes for (auto& cv : m_chunks) { m_mesher.freeChunkMesh(cv.chunkMesh); cv.chunkMesh = m_mesher.easyCreateChunkMesh(cv.chunk, MeshTaskType::DEFAULT); } break; case VKEY_F11: // Reload shaders m_renderer.dispose(); m_renderer.init(); m_ssaoStage.reloadShaders(); break; } }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch (e.keyCode) { case VKEY_W: m_movingForward = false; break; case VKEY_S: m_movingBack = false; break; case VKEY_A: m_movingLeft = false; break; case VKEY_D: m_movingRight = false; break; case VKEY_SPACE: m_movingUp = false; break; case VKEY_LSHIFT: m_movingFast = false; break; } }); } Chunk* TestConnectedTextureScreen::addChunk(const nString& name) { Chunk* chunk = new Chunk; // TODO(Ben): This is wrong now, need accessor chunk->initAndFillEmpty(WorldCubeFace::FACE_TOP); // TODO(Ben): AOS ViewableChunk viewable; viewable.chunk = chunk; viewable.name = name; m_chunks.push_back(viewable); return chunk; } ================================================ FILE: SoA/TestConnectedTextureScreen.h ================================================ /// /// TestConnectedTextureScreen.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 2 Jul 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Test screen to help artists with connected textures /// #pragma once #ifndef TestConnectedTextureScreen_h__ #define TestConnectedTextureScreen_h__ #include #include #include #include #include #include #include #include "BlockPack.h" #include "Camera.h" #include "Chunk.h" #include "CommonState.h" #include "ChunkMesher.h" #include "ChunkRenderer.h" #include "SsaoRenderStage.h" #include "HdrRenderStage.h" class TestConnectedTextureScreen : public vui::IAppScreen { public: TestConnectedTextureScreen(const App* app, CommonState* state); /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ i32 getNextScreen() const override; i32 getPreviousScreen() const override; void build() override; void destroy(const vui::GameTime& gameTime) override; void onEntry(const vui::GameTime& gameTime) override; void onExit(const vui::GameTime& gameTime) override; void update(const vui::GameTime& gameTime) override; void draw(const vui::GameTime& gameTime) override; private: void initChunks(); void initInput(); Chunk* addChunk(const nString& name); struct ViewableChunk { Chunk* chunk; ChunkMesh* chunkMesh; nString name; }; AutoDelegatePool m_hooks; ///< Input hooks reservoir Camera m_camera; BlockPack m_blocks; ///< Block data CommonState* m_commonState; SoaState* m_soaState; ChunkRenderer m_renderer; ChunkMesher m_mesher; vg::SpriteBatch m_sb; vg::SpriteFont m_font; std::vector m_chunks; vg::GBuffer m_hdrTarget; ///< Framebuffer needed for the HDR rendering vg::RTSwapChain<2> m_swapChain; ///< Swap chain of framebuffers used for post-processing SSAORenderStage m_ssaoStage; HdrRenderStage m_hdrStage; bool m_wireFrame = false; bool m_mouseButtons[2]; bool m_movingForward = false; bool m_movingBack = false; bool m_movingLeft = false; bool m_movingRight = false; bool m_movingUp = false; bool m_movingDown = false; bool m_movingFast = false; int m_activeChunk = 0; }; #endif // TestConnectedTextureScreen_h__ ================================================ FILE: SoA/TestConsoleScreen.cpp ================================================ #include "stdafx.h" #include "TestConsoleScreen.h" #include i32 TestConsoleScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestConsoleScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestConsoleScreen::build() { // Empty } void TestConsoleScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestConsoleScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { // #ifdef VORB_LUA // m_delegatePool.addAutoHook(m_console.onStream[DEV_CONSOLE_STREAM_OUT], [&] (Sender sender VORB_MAYBE_UNUSED, const cString s) { // printf("Out: %s\n", s); // }); // m_delegatePool.addAutoHook(m_console.onStream[DEV_CONSOLE_STREAM_ERR], [&] (Sender sender VORB_MAYBE_UNUSED, const cString s) { // printf("Err: %s\n", s); // }); // m_delegatePool.addAutoHook(m_text.onTextChange, [&] (Sender sender VORB_MAYBE_UNUSED, const cString s) { // printf("\rInput: %s ", s); // }); // m_delegatePool.addAutoHook(m_text.onTextEntry, [&] (Sender sender VORB_MAYBE_UNUSED, const cString s) { // printf("\rComm: %s\n", s); // m_console.invokeCommand(s); // }); // m_text.start(); // printf("Welcome to Lua REPL\nInput: "); // #endif//VORB_LUA glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0); } void TestConsoleScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_text.stop(); m_delegatePool.dispose(); } void TestConsoleScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestConsoleScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } ================================================ FILE: SoA/TestConsoleScreen.h ================================================ /// /// TestConsoleScreen.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 14 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Tests out the Lua dev console /// #pragma once #ifndef TestConsoleScreen_h__ #define TestConsoleScreen_h__ #include #include // #include #include // TODO(Matthew): Implement. class TestConsoleScreen : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: // #ifdef VORB_LUA // vui::LuaDevConsole m_console; ///< Console used for testing // #endif//VORB_LUA vui::TextInputListener m_text; ///< Text input AutoDelegatePool m_delegatePool; ///< Input hooks reservoir }; #endif // TestConsoleScreen_h__ ================================================ FILE: SoA/TestDeferredScreen.cpp ================================================ #include "stdafx.h" #include "TestDeferredScreen.h" #include "ShaderLoader.h" #include #include #include #include #include #include #include #include // Number cells per row/column in a single grid const ui32 CELLS = 20; vg::DepthState dsLightPoint(true, vg::DepthFunction::GREATER, false); vg::DepthState dsLightDirectional(true, vg::DepthFunction::NOTEQUAL, false); struct DefVertex { public: f32v3 position; f32v3 normal; }; TestDeferredScreen::TestDeferredScreen() : m_sb(true, false) { // Empty } i32 TestDeferredScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestDeferredScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestDeferredScreen::build() { // Empty } void TestDeferredScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestDeferredScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_eyePos = f32v3(0, 0, 4); buildGeometry(); buildLightMaps(); m_gbuffer = vg::GBuffer(m_game->getWindow().getWidth(), m_game->getWindow().getHeight()); Array ga; ga.setData(4); ga[0].format = vg::TextureInternalFormat::RGBA16F; ga[0].pixelFormat = vg::TextureFormat::RGBA; ga[0].pixelType = vg::TexturePixelType::HALF_FLOAT; ga[0].number = 0; ga[1] = ga[0]; ga[1].number = 1; ga[2].format = vg::TextureInternalFormat::RG32F; ga[2].pixelFormat = vg::TextureFormat::RG; ga[2].pixelType = vg::TexturePixelType::FLOAT; ga[2].number = 2; m_gbuffer.init(ga, vg::TextureInternalFormat::RGB16F).initDepthStencil(); m_sb.init(); { // Init Shaders m_deferredPrograms.clear = ShaderLoader::createProgramFromFile("Shaders/Deferred/Clear.vert", "Shaders/Deferred/Clear.frag"); m_deferredPrograms.composition = ShaderLoader::createProgramFromFile("Shaders/Deferred/Composition.vert", "Shaders/Deferred/Composition.frag"); m_deferredPrograms.geometry["Basic"] = ShaderLoader::createProgramFromFile("Shaders/Deferred/Geometry.vert", "Shaders/Deferred/Geometry.frag"); m_deferredPrograms.light["Directional"] = ShaderLoader::createProgramFromFile("Shaders/Deferred/LightDirectional.vert", "Shaders/Deferred/LightDirectional.frag"); } m_quad.init(0); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&] (Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch (e.keyCode) { case VKEY_I: if(m_roughness < 0.96) m_roughness += 0.05f; break; case VKEY_J: if (m_roughness > 0.04) m_roughness -= 0.05f; break; case VKEY_O: if (m_reflectance < 0.91) m_reflectance += 0.1f; break; case VKEY_K: if (m_reflectance > 0.09) m_reflectance -= 0.1f; break; case VKEY_P: if (m_metalness < 0.5) m_metalness = 1; break; case VKEY_L: if (m_metalness > 0.5) m_metalness = 0; break; default: break; } }); m_reflectance = 0.0f; m_roughness = 1.0f; m_metalness = 1.0f; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0); } void TestDeferredScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glDeleteBuffers(1, &m_verts); glDeleteBuffers(1, &m_inds); m_deferredPrograms.dispose(); m_sb.dispose(); m_quad.dispose(); m_gbuffer.dispose(); } void TestDeferredScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { // Empty f32v4 rotated = f32v4(m_eyePos, 1) * glm::rotate(f32m4(), (float)(10 * gameTime.elapsed), f32v3(0, 1, 0)); m_eyePos.x = rotated.x; m_eyePos.y = rotated.y; m_eyePos.z = rotated.z; } void TestDeferredScreen::draw(const vui::GameTime& gameTime VORB_UNUSED) { /************************************************************************/ /* Deferred pass */ /************************************************************************/ m_gbuffer.useGeometry(); // Clear the GBuffer glBlendFunc(GL_ONE, GL_ZERO); vg::DepthState::WRITE.set(); m_deferredPrograms.clear.use(); m_quad.draw(); f32m4 mVP = glm::perspectiveFov(90.0f, 800.0f, 600.0f, 0.1f, 1000.0f) * glm::lookAt(m_eyePos, f32v3(0, 0, 0), f32v3(0, 1, 0)); f32m4 mVPInv = glm::inverse(mVP); vg::DepthState::FULL.set(); vg::RasterizerState::CULL_CLOCKWISE.set(); vg::GLProgram& progGeo = m_deferredPrograms.geometry["Basic"]; progGeo.use(); progGeo.enableVertexAttribArrays(); glUniformMatrix4fv(progGeo.getUniform("unVP"), 1, false, (f32*)&mVP[0][0]); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glVertexAttribPointer(progGeo.getAttribute("vPosition"), 3, GL_FLOAT, false, sizeof(DefVertex), offsetptr(DefVertex, position)); glVertexAttribPointer(progGeo.getAttribute("vNormal"), 3, GL_FLOAT, false, sizeof(DefVertex), offsetptr(DefVertex, normal)); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); f32m4 mW = glm::translate(f32m4(1.0f), f32v3(-1.3f, -1, 0)); f32m3 mWIT = f32m3(glm::transpose(glm::inverse(mW))); glUniformMatrix4fv(progGeo.getUniform("unWorld"), 1, false, (f32*)&mW[0][0]); glUniformMatrix3fv(progGeo.getUniform("unWorldIT"), 1, false, (f32*)&mWIT[0][0]); glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr); mW = glm::translate(f32m4(1.0f), f32v3(1.3f, 1, 0)); mWIT = f32m3(glm::transpose(glm::inverse(mW))); glUniformMatrix4fv(progGeo.getUniform("unWorld"), 1, false, (f32*)&mW[0][0]); glUniformMatrix3fv(progGeo.getUniform("unWorldIT"), 1, false, (f32*)&mWIT[0][0]); glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr); mW = glm::translate(f32m4(1.0f), f32v3(0, 0, -2)); mWIT = f32m3(glm::transpose(glm::inverse(mW))); glUniformMatrix4fv(progGeo.getUniform("unWorld"), 1, false, (f32*)&mW[0][0]); glUniformMatrix3fv(progGeo.getUniform("unWorldIT"), 1, false, (f32*)&mWIT[0][0]); glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); progGeo.disableVertexAttribArrays(); /************************************************************************/ /* Lighting pass */ /************************************************************************/ m_gbuffer.useLight(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glBlendFunc(GL_ONE, GL_ONE); { dsLightDirectional.set(); vg::GLProgram& progLight = m_deferredPrograms.light["Directional"]; progLight.use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getGeometryTexture(1)); glUniform1i(progLight.getUniform("unTexNormal"), 0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getGeometryTexture(2)); glUniform1i(progLight.getUniform("unTexDepth"), 1); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_CUBE_MAP, m_envMap); glUniform1i(progLight.getUniform("unTexEnvironment"), 2); glUniformMatrix4fv(progLight.getUniform("unVPInv"), 1, false, (f32*)&mVPInv[0][0]); glUniform3f(progLight.getUniform("unEyePosition"), m_eyePos.x, m_eyePos.y, m_eyePos.z); glUniform1f(progLight.getUniform("unRoughness"), m_roughness); glUniform1f(progLight.getUniform("unReflectance"), m_reflectance * 0.96f + 0.04f); glUniform1f(progLight.getUniform("unMetalness"), m_metalness); //const size_t NUM_LIGHTS = 3; //f32v3 lightDirs[NUM_LIGHTS] = { // f32v3(0, 0, -1), // f32v3(2, -1, 0), // f32v3(-1, 1, -1) // //f32v3(-4, -3, 0), // //f32v3(6, 3, 2) //}; //f32v3 lightColors[NUM_LIGHTS] = { // f32v3(0.6, 0.6, 0.3), // f32v3(1.0, 0.0, 0.0), // f32v3(0.0, 1.0, 0.0) // //f32v3(0.0, 1.0, 1.0), // //f32v3(1.0, 0.0, 1.0) //}; //for (size_t i = 0; i < NUM_LIGHTS; i++) { // f32v3 lightDir = glm::normalize(lightDirs[i]); // glUniform3f(progLight.getUniform("unLightDirection"), lightDir.x, lightDir.y, lightDir.z); // f32v3 lightColor = lightColors[i]; // glUniform3f(progLight.getUniform("unLightIntensity"), lightColor.x, lightColor.y, lightColor.z); //} m_quad.draw(); } { dsLightPoint.set(); /*vg::GLProgram& progLight = m_deferredPrograms.light["Point"]; progLight.use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getGeometryTexture(1)); glUniform1i(progLight.getUniform("unTexNormal"), 0); const size_t NUM_LIGHTS = 5; f32v3 lightDirs[NUM_LIGHTS] = { f32v3(0, -2, -1), f32v3(2, -1, 0), f32v3(-1, 1, -1), f32v3(-4, -3, 0), f32v3(6, 3, 2) }; f32v3 lightColors[NUM_LIGHTS] = { f32v3(0.6, 0.6, 0.3), f32v3(1.0, 0.0, 0.0), f32v3(0.0, 1.0, 0.0), f32v3(0.0, 1.0, 1.0), f32v3(1.0, 0.0, 1.0) }; for (size_t i = 0; i < NUM_LIGHTS; i++) { f32v3 lightDir = glm::normalize(lightDirs[i]); glUniform3f(progLight.getUniform("unLightDirection"), lightDir.x, lightDir.y, lightDir.z); f32v3 lightColor = lightColors[i]; glUniform3f(progLight.getUniform("unLightColor"), lightColor.x, lightColor.y, lightColor.z); m_quad.draw(); }*/ } /************************************************************************/ /* Compose deferred and lighting */ /************************************************************************/ vg::GLRenderTarget::unuse(m_game->getWindow().getWidth(), m_game->getWindow().getHeight()); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBlendFunc(GL_ONE, GL_ZERO); vg::DepthState::WRITE.set(); m_deferredPrograms.composition.use(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getGeometryTexture(0)); glUniform1i(m_deferredPrograms.composition.getUniform("unTexColor"), 0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getGeometryTexture(2)); glUniform1i(m_deferredPrograms.composition.getUniform("unTexDepth"), 1); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_gbuffer.getLightTexture()); glUniform1i(m_deferredPrograms.composition.getUniform("unTexLight"), 2); m_quad.draw(); /************************************************************************/ /* Debug */ /************************************************************************/ m_sb.begin(); m_sb.draw(m_gbuffer.getGeometryTexture(0), f32v2(0, 0), f32v2(200, 150), color::White); m_sb.draw(m_gbuffer.getGeometryTexture(1), f32v2(200, 0), f32v2(200, 150), color::White); m_sb.draw(m_gbuffer.getGeometryTexture(2), f32v2(400, 0), f32v2(200, 150), color::White); m_sb.draw(m_gbuffer.getLightTexture(), f32v2(600, 0), f32v2(200, 150), color::White); m_sb.end(); m_sb.render(f32v2(m_game->getWindow().getWidth(), m_game->getWindow().getHeight())); } void TestDeferredScreen::buildGeometry() { // Make vertices ui32 cellPoints = CELLS + 1; ui32 gridPoints = cellPoints * cellPoints; DefVertex* verts = new DefVertex[gridPoints * 2]; size_t i = 0; for (size_t y = 0; y < cellPoints; y++) { for (size_t x = 0; x < cellPoints; x++) { f32v3 pos((f32)x / (f32)CELLS, 0, (f32)y / (f32)CELLS); pos.x -= 0.5f; pos.x *= 2.0f; pos.z -= 0.5f; pos.z *= 2.0f; pos.x = glm::sign(pos.x) * (sin(abs(pos.x) * 1.57079f)); pos.z = glm::sign(pos.z) * (sin(abs(pos.z) * 1.57079f)); f32 lp = glm::length(pos); if (lp < 0.00001) { pos.y = 1.0f; } else { f32 d = std::max(abs(pos.x), abs(pos.z)); pos /= lp; pos *= d; pos.y = sin(acos(d)); } verts[i].position = pos; verts[i].normal = glm::normalize(verts[i].position); pos.y = -pos.y; verts[gridPoints + i].position = pos; verts[gridPoints + i].normal = glm::normalize(verts[gridPoints + i].position); i++; } } // Make indices ui32 gridInds = 6 * CELLS * CELLS; m_indexCount = gridInds * 2; ui32* inds = new ui32[m_indexCount]; i = 0; for (size_t y = 0; y < CELLS; y++) { for (size_t x = 0; x < CELLS; x++) { ui32 vi = y * cellPoints + x; inds[i] = vi; inds[i + 1] = vi + cellPoints; inds[i + 2] = vi + 1; inds[i + 3] = vi + 1; inds[i + 4] = vi + cellPoints; inds[i + 5] = vi + cellPoints + 1; vi += gridPoints; inds[gridInds + i] = vi; inds[gridInds + i + 1] = vi + 1; inds[gridInds + i + 2] = vi + cellPoints; inds[gridInds + i + 3] = vi + cellPoints; inds[gridInds + i + 4] = vi + 1; inds[gridInds + i + 5] = vi + cellPoints + 1; i += 6; } } // Fill buffers glGenBuffers(1, &m_verts); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glBufferData(GL_ARRAY_BUFFER, gridPoints * 2 * sizeof(DefVertex), verts, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); delete[] verts; glGenBuffers(1, &m_inds); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(ui32), inds, GL_STATIC_DRAW); delete[] inds; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } void TestDeferredScreen::buildLightMaps() { glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); glGenTextures(1, &m_envMap); glBindTexture(GL_TEXTURE_CUBE_MAP, m_envMap); vg::ImageIO imageLoader; vg::ScopedBitmapResource rs0(imageLoader.load("Textures/Test/nx.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB16F, rs0.width, rs0.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs0.data); vg::ScopedBitmapResource rs1(imageLoader.load("Textures/Test/px.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB16F, rs1.width, rs1.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs1.data); vg::ScopedBitmapResource rs2(imageLoader.load("Textures/Test/ny.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB16F, rs2.width, rs2.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs2.data); vg::ScopedBitmapResource rs3(imageLoader.load("Textures/Test/py.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB16F, rs3.width, rs3.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs3.data); vg::ScopedBitmapResource rs4(imageLoader.load("Textures/Test/nz.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB16F, rs4.width, rs4.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs4.data); vg::ScopedBitmapResource rs5(imageLoader.load("Textures/Test/pz.png")); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB16F, rs5.width, rs5.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rs5.data); vg::SamplerState ss( (VGEnum)vg::TextureMinFilter::LINEAR_MIPMAP_LINEAR, (VGEnum)vg::TextureMagFilter::LINEAR, (VGEnum)vg::TextureWrapMode::REPEAT, (VGEnum)vg::TextureWrapMode::REPEAT, (VGEnum)vg::TextureWrapMode::REPEAT ); ss.set(GL_TEXTURE_CUBE_MAP); glGenerateMipmap(GL_TEXTURE_CUBE_MAP); } ================================================ FILE: SoA/TestDeferredScreen.h ================================================ /// /// TestDeferredScreen.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 16 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Development test for deferred rendering /// #pragma once #ifndef TestDeferredScreen_h__ #define TestDeferredScreen_h__ #include #include #include #include #include #include #include #include class TestDeferredScreen : public vui::IGameScreen { public: /// TestDeferredScreen(); /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: void buildGeometry(); void buildLightMaps(); f32v3 m_eyePos; VGVertexBuffer m_verts; ///< Sphere's vertex buffer (of positions) VGIndexBuffer m_inds; ///< Sphere's index buffer ui32 m_indexCount; ///< Number of indices for sphere vg::DeferredShaders m_deferredPrograms; ///< Basic rendering programs vg::FullQuadVBO m_quad; ///< Used for GBuffer clearing operations vg::GBuffer m_gbuffer; ///< Geometry buffer of deferred rendering vg::SpriteBatch m_sb; ///< Debug SpriteBatch AutoDelegatePool m_hooks; ///< Input hooks reservoir VGTexture m_envMap; ///< Environment map f32 m_roughness, m_reflectance, m_metalness; ///< Temp test values }; #endif // TestDeferredScreen_h__ ================================================ FILE: SoA/TestDisplacementMappingScreen.cpp ================================================ #include "stdafx.h" #include "TestDisplacementMappingScreen.h" #include #include #include #include "Errors.h" #include "ShaderLoader.h" #include const char* vertexShader = R"( uniform mat4 unModelMatrix; uniform mat4 unMVP; in vec3 vPosition; in vec3 vNormal; in vec3 vTangent; in vec2 vUV; out vec3 fPosition; out vec2 fUV; out mat3 fTbnMatrix; void main() { fPosition = (unModelMatrix * vec4(vPosition, 1.0)).xyz; fUV = vec2(vUV.x, vUV.y); vec3 normal = normalize((unModelMatrix * vec4(vNormal, 0.0)).xyz); vec3 tangent = normalize((unModelMatrix * vec4(vTangent, 0.0)).xyz); tangent = normalize(tangent - dot(tangent, normal) * normal); vec3 biTangent = cross(tangent, normal); fTbnMatrix = mat3(tangent, biTangent, normal); gl_Position = unMVP * vec4(vPosition, 1.0); } )"; const char* fragmentShader = R"( uniform vec3 unEyePosition; uniform sampler2D unDiffuseTexture; uniform sampler2D unNormalTexture; uniform sampler2D unDispTexture; uniform float unDispScale; in vec3 fPosition; in vec2 fUV; in mat3 fTbnMatrix; out vec4 pColor; vec2 calculateOffset(vec2 uv, sampler2D dispTexture, mat3 tbnMatrix, vec3 directionToEye, float scale, float bias) { return (directionToEye * tbnMatrix).xy * (texture(dispTexture, uv).r * scale + bias); } void main() { vec3 directionToEye = normalize(unEyePosition - fPosition); float bias = -unDispScale / 2.0; vec2 offsetUV = fUV; if (gl_FragCoord.x > 1920 / 2.0) offsetUV += calculateOffset(fUV, unDispTexture, fTbnMatrix, directionToEye, unDispScale, bias); vec3 correctNormal = fTbnMatrix[2]; if (gl_FragCoord.y > 1080.0 / 2.0) correctNormal = fTbnMatrix * normalize(texture2D(unNormalTexture, offsetUV).rgb * 2.0 - 1.0); vec3 directionToLight = vec3(0.0, 0.0, -2.5) - fPosition; float light = dot(correctNormal, normalize(directionToLight)) / (length(directionToLight) + 1.0) * 4.0; pColor = vec4(texture(unDiffuseTexture, offsetUV).rgb * light, 1.0); } )"; i32 TestDisplacementMappingScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestDisplacementMappingScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestDisplacementMappingScreen::build() { } void TestDisplacementMappingScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestDisplacementMappingScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_displacementScale = 0.08f; m_view = f32v3(0.0f); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onWheel, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseWheelEvent& e) { m_displacementScale += e.dy * 0.01f; if (m_displacementScale < 0.0f) m_displacementScale = 0.0f; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_ldown = true; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_ldown = false; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { if (m_ldown) { m_view.x += e.dy * 0.1f; m_view.y += e.dx * 0.1f; } }); m_camera.setPosition(f64v3(0.0f, 0.0f, 0.0f)); m_camera.setFieldOfView(90.0f); m_camera.setAspectRatio(m_game->getWindow().getAspectRatio()); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); m_camera.setRight(f32v3(1.0f, 0.0f, 0.0f)); m_camera.setClippingPlane(0.1f, 16.0f); m_camera.update(); m_program = ShaderLoader::createProgram("ParallaxDisplacementMapping", vertexShader, fragmentShader); vg::BitmapResource rs = vg::ImageIO().load("Textures/Test/stone.png"); if (rs.data == nullptr) pError("Failed to load texture"); m_diffuseTexture = vg::GpuMemory::uploadTexture(&rs, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); vg::ImageIO().free(rs); rs = vg::ImageIO().load("Textures/Test/stone_NRM.png"); if (rs.data == nullptr) pError("Failed to load texture"); m_normalTexture = vg::GpuMemory::uploadTexture(&rs, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); vg::ImageIO().free(rs); rs = vg::ImageIO().load("Textures/Test/stone_DISP.png"); if (rs.data == nullptr) pError("Failed to load texture"); m_displacementTexture = vg::GpuMemory::uploadTexture(&rs, vg::TexturePixelType::UNSIGNED_BYTE, vg::TextureTarget::TEXTURE_2D, &vg::SamplerState::LINEAR_WRAP_MIPMAP); vg::ImageIO().free(rs); float anisotropy; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_diffuseTexture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_normalTexture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, m_displacementTexture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); } void TestDisplacementMappingScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_program.dispose(); glDeleteTextures(1, &m_diffuseTexture); glDeleteTextures(1, &m_normalTexture); glDeleteTextures(1, &m_displacementTexture); } void TestDisplacementMappingScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { } void TestDisplacementMappingScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_program.use(); //m_displacementScale = (sinf(gameTime.total * 4.0) * 0.5f + 0.5f) * 0.1f; f32m4 unModelMatrix = glm::translate(f32v3(0.0f, 0.0f, -3.0f - m_view.z)) * glm::rotate(m_view.x, f32v3(1.0f, 0.0f, 0.0f)) * glm::rotate(m_view.y, f32v3(0.0f, 1.0f, 0.0f)); glUniformMatrix4fv(m_program.getUniform("unModelMatrix"), 1, false, (f32*)&unModelMatrix[0][0]); f32m4 unMVP = m_camera.getViewProjectionMatrix() * unModelMatrix; glUniformMatrix4fv(m_program.getUniform("unMVP"), 1, false, (f32*)&unMVP[0][0]); glUniform3f(m_program.getUniform("unEyePosition"), (f32)m_camera.getPosition().x, (f32)m_camera.getPosition().y, (f32)m_camera.getPosition().z); glUniform1i(m_program.getUniform("unDiffuseTexture"), 0); glUniform1i(m_program.getUniform("unNormalTexture"), 1); glUniform1i(m_program.getUniform("unDispTexture"), 2); glUniform1f(m_program.getUniform("unDispScale"), m_displacementScale); glBegin(GL_QUADS); float tangentDir = 1.0f; glVertexAttrib3f(0, -1.0f, 1.0f, 0.0f); glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); glVertexAttrib3f(2, 0.0f, tangentDir, 0.0f); glVertexAttrib2f(3, 0.0f, 0.0f); glVertexAttrib3f(0, -1.0f, -1.0f, 0.0f); glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); glVertexAttrib3f(2, 0.0f, tangentDir, 0.0f); glVertexAttrib2f(3, 0.0f, 1.0f); glVertexAttrib3f(0, 1.0f, -1.0f, 0.0f); glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); glVertexAttrib3f(2, 0.0f, tangentDir, 0.0f); glVertexAttrib2f(3, 1.0f, 1.0f); glVertexAttrib3f(0, 1.0f, 1.0f, 0.0f); glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f); glVertexAttrib3f(2, 0.0f, tangentDir, 0.0f); glVertexAttrib2f(3, 1.0f, 0.0f); glEnd(); m_program.unuse(); checkGlError("TestDisplacementMappingScreen::draw"); } ================================================ FILE: SoA/TestDisplacementMappingScreen.h ================================================ #pragma once #ifndef TestDisplacementMappingScreen_h__ #define TestDisplacementMappingScreen_h__ #include #include #include "Camera.h" class TestDisplacementMappingScreen : public vui::IGameScreen { public: virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: Camera m_camera; vg::GLProgram m_program; VGTexture m_diffuseTexture; VGTexture m_normalTexture; VGTexture m_displacementTexture; float m_displacementScale; AutoDelegatePool m_hooks; bool m_ldown; f32v3 m_view; }; #endif ================================================ FILE: SoA/TestGasGiantScreen.cpp ================================================ #include "stdafx.h" #include "TestGasGiantScreen.h" #include "soaUtils.h" #include "Errors.h" #include #include #include #include #include #include #include i32 TestGasGiantScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestGasGiantScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestGasGiantScreen::build() { } void TestGasGiantScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestGasGiantScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if(e.keyCode==VKEY_F1) { m_gasGiantRenderer.dispose(); m_gasGiantRenderer.initGL(); m_atmoRenderer.dispose(); m_atmoRenderer.initGL(); } else if(e.keyCode==VKEY_A) { m_isAtmosphere=!m_isAtmosphere; } else if(e.keyCode==VKEY_ESCAPE) { exit(0); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onWheel, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseWheelEvent& e) { m_eyeDist += -e.dy * 0.025 * m_eyeDist; }); glEnable(GL_DEPTH_TEST); // vg::BitmapResource rs = vg::ImageIO().load("Textures/Test/GasGiantLookup2.png"); // if (rs.data == nullptr) pError("Failed to load gas giant texture"); // VGTexture colorBandLookup = vg::GpuMemory::uploadTexture(&rs, // vg::TexturePixelType::UNSIGNED_BYTE, // vg::TextureTarget::TEXTURE_2D, // &vg::SamplerState::LINEAR_CLAMP); // // vg::ImageIO().free(rs); glClearColor(0, 0, 0, 1); glClearDepth(1.0); m_eyePos = f32v3(0, 0, m_eyeDist); // Set up components m_ggCmp.radius = (f32)GIANT_RADIUS; m_ggCmp.oblateness=0.0; m_ggCmp.colorMapPath="Textures/Test/GasGiantLookup2.png"; m_aCmp.radius = (f32)(GIANT_RADIUS * 1.025); m_aCmp.planetRadius = (f32)GIANT_RADIUS; m_aCmp.invWavelength4 = f32v3(1.0f / powf(0.475f, 4.0f), 1.0f / powf(0.57f, 4.0f), 1.0f / powf(0.65f, 4.0f)); m_camera.setFieldOfView(90.0f); f32 width = (f32)m_game->getWindow().getWidth(); f32 height = (f32)m_game->getWindow().getHeight(); m_camera.setAspectRatio(width / height); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); m_gasGiantRenderer.initGL(); m_atmoRenderer.initGL(); } void TestGasGiantScreen::onExit(const vui::GameTime& gameTime VORB_UNUSED) { } void TestGasGiantScreen::update(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_eyePos = f64v3(0, 0, GIANT_RADIUS + m_eyeDist + 100.0); } void TestGasGiantScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_camera.setClippingPlane((f32)(m_eyeDist / 2.0), (f32)(m_eyeDist + GIANT_RADIUS * 10.0)); m_camera.setPosition(f64v3(m_eyePos)); m_camera.update(); f32v3 lightPos = glm::normalize(f32v3(0.0f, 0.0f, 1.0f)); PreciseTimer timer; m_gasGiantRenderer.draw(m_ggCmp, 0, m_camera.getViewProjectionMatrix(), f64q(), f32v3(m_eyePos), lightPos, computeZCoef(m_camera.getFarClip()), &m_slCmp, &m_aCmp); if(m_isAtmosphere) m_atmoRenderer.draw(m_aCmp, m_camera.getViewProjectionMatrix(), f32v3(m_eyePos), lightPos, computeZCoef(m_camera.getFarClip()), &m_slCmp); //glFinish(); checkGlError("TestGasGiantScreen::draw"); } ================================================ FILE: SoA/TestGasGiantScreen.h ================================================ #pragma once #ifndef TestGasGiantScreen_h__ #define TestGasGiantScreen_h__ #include "AtmosphereComponentRenderer.h" #include "Camera.h" #include "GasGiantComponentRenderer.h" #include "SpaceSystemComponents.h" #include #include #include #include #include #include #include class GasGiantRenderer; DECL_VIO(class IOManager); class TestGasGiantScreen : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: const f64 GIANT_RADIUS = 154190.0 / 2.0; GasGiantComponentRenderer m_gasGiantRenderer; AtmosphereComponentRenderer m_atmoRenderer; f64v3 m_eyePos; f64 m_eyeDist = GIANT_RADIUS; GasGiantComponent m_ggCmp; SpaceLightComponent m_slCmp; AtmosphereComponent m_aCmp; Camera m_camera; AutoDelegatePool m_hooks; bool m_isAtmosphere=true; }; #endif ================================================ FILE: SoA/TestMappingScreen.cpp ================================================ #include "stdafx.h" #include "TestMappingScreen.h" #include #include #ifdef _MSC_VER #pragma region Simple shader code #endif// _MSC_VER const cString SRC_VERT = R"( uniform mat4 unWVP; in vec4 vPosition; void main() { gl_Position = unWVP * vPosition; } )"; const cString SRC_FRAG = R"( out vec4 pColor; void main() { pColor = vec4(1, 0, 0, 1); } )"; #ifdef _MSC_VER #pragma endregion #endif// _MSC_VER // Number cells per row/column in a single grid const ui32 CELLS = 30; i32 TestMappingScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestMappingScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestMappingScreen::build() { // Empty } void TestMappingScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestMappingScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { buildGeometry(); m_program.init(); m_program.addShader(vg::ShaderType::VERTEX_SHADER, SRC_VERT); m_program.addShader(vg::ShaderType::FRAGMENT_SHADER, SRC_FRAG); m_program.link(); m_program.initAttributes(); m_program.initUniforms(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0); } void TestMappingScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glDeleteBuffers(1, &m_verts); glDeleteBuffers(1, &m_inds); m_program.dispose(); } void TestMappingScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestMappingScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); f32m4 mWVP = glm::perspectiveFov(90.0f, 800.0f, 600.0f, 0.1f, 100.0f) * glm::lookAt(f32v3(0, 0, 10), f32v3(0, 0, 0), f32v3(0, 1, 0)) ; vg::DepthState::FULL.set(); vg::RasterizerState::CULL_CLOCKWISE.set(); m_program.use(); m_program.enableVertexAttribArrays(); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, false, (f32*)&mWVP[0][0]); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, false, 0, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); glDrawElements(GL_TRIANGLES, m_indexCount, GL_UNSIGNED_INT, nullptr); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); m_program.disableVertexAttribArrays(); vg::GLProgram::unuse(); } void TestMappingScreen::buildGeometry() { // Make vertices ui32 cellPoints = CELLS + 1; ui32 gridPoints = cellPoints * cellPoints; f32v3* verts = new f32v3[gridPoints * 2]; size_t i = 0; for (size_t y = 0; y < cellPoints; y++) { for (size_t x = 0; x < cellPoints; x++) { f32v3 pos((f32)x / (f32)CELLS, 0, (f32)y / (f32)CELLS); pos.x -= 0.5f; pos.x *= 2.0f; pos.z -= 0.5f; pos.z *= 2.0f; pos.x = glm::sign(pos.x) * (sin(abs(pos.x) * 1.57079f)); pos.z = glm::sign(pos.z) * (sin(abs(pos.z) * 1.57079f)); f32 lp = glm::length(pos); if (lp < 0.00001) { pos.y = 1.0f; } else { f32 d = std::max(abs(pos.x), abs(pos.z)); pos /= lp; pos *= d; pos.y = sin(acos(d)); } verts[i] = pos; pos.y = -pos.y; verts[gridPoints + i] = pos; i++; } } // Make indices ui32 gridInds = 6 * CELLS * CELLS; m_indexCount = gridInds * 2; ui32* inds = new ui32[m_indexCount]; i = 0; for (size_t y = 0; y < CELLS; y++) { for (size_t x = 0; x < CELLS; x++) { ui32 vi = y * cellPoints + x; inds[i] = vi; inds[i + 1] = vi + cellPoints; inds[i + 2] = vi + 1; inds[i + 3] = vi + 1; inds[i + 4] = vi + cellPoints; inds[i + 5] = vi + cellPoints + 1; vi += gridPoints; inds[gridInds + i] = vi; inds[gridInds + i + 1] = vi + 1; inds[gridInds + i + 2] = vi + cellPoints; inds[gridInds + i + 3] = vi + cellPoints; inds[gridInds + i + 4] = vi + 1; inds[gridInds + i + 5] = vi + cellPoints + 1; i += 6; } } // Fill buffers glGenBuffers(1, &m_verts); glBindBuffer(GL_ARRAY_BUFFER, m_verts); glBufferData(GL_ARRAY_BUFFER, gridPoints * 2 * sizeof(f32v3), verts, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); delete[] verts; glGenBuffers(1, &m_inds); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_inds); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(ui32), inds, GL_STATIC_DRAW); delete[] inds; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } ================================================ FILE: SoA/TestMappingScreen.h ================================================ /// /// TestMappingScreen.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 14 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Render test of grid-mapped sphere /// #pragma once #ifndef TestMappingScreen_h__ #define TestMappingScreen_h__ #include #include #include class TestMappingScreen : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: void buildGeometry(); VGVertexBuffer m_verts; ///< Sphere's vertex buffer (of positions) VGIndexBuffer m_inds; ///< Sphere's index buffer ui32 m_indexCount; ///< Number of indices for sphere vg::GLProgram m_program; ///< Basic rendering program }; #endif // TestMappingScreen_h__ ================================================ FILE: SoA/TestNewBlockAPIScreen.cpp ================================================ #include "stdafx.h" #include "TestNewBlockAPIScreen.h" #include "Positional.h" i32 TestNewBlockAPIScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestNewBlockAPIScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestNewBlockAPIScreen::build() { // Empty } void TestNewBlockAPIScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestNewBlockAPIScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { VoxelIterablePosition pos { 0, 1, 2 }; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); pos.x += 5; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); pos.x += 35; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); pos.wx() += 0; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); pos.cx() += 200; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); (pos.wx() += 200).rz() += 200; printf("Pos: %d,%d,%d\n", pos.x, pos.y, pos.z); } void TestNewBlockAPIScreen::onExit(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestNewBlockAPIScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestNewBlockAPIScreen::draw(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } ================================================ FILE: SoA/TestNewBlockAPIScreen.h ================================================ // // TestNewBlockAPIScreen.h // Seed of Andromeda // // Created by Cristian Zaloj on 26 May 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef TestNewBlockAPIScreen_h__ #define TestNewBlockAPIScreen_h__ #include class TestNewBlockAPIScreen : public vui::IGameScreen { public: virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; }; #endif // TestNewBlockAPIScreen_h__ ================================================ FILE: SoA/TestNoiseScreen.cpp ================================================ #include "stdafx.h" #include "TestNoiseScreen.h" #include #include #include #include #include "Errors.h" #include "ShaderLoader.h" #include "Noise.h" #include "soaUtils.h" #define MS_AVARAGE_FRAMES 60 auto startTime = std::chrono::high_resolution_clock::now(); f64 ms = 0; unsigned int frameCount = 0; TestNoiseScreen::TestNoiseScreen(const App* app) : IAppScreen(app) { } i32 TestNoiseScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestNoiseScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestNoiseScreen::build() { } void TestNoiseScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestNoiseScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_sb.init(); m_font.init("Fonts/orbitron_bold-webfont.ttf", 32); for (int i = 0; i < NUM_TEST_NOISE_TYPES; i++) { m_textures[i] = 0; } onNoiseChange(); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_LEFT) { if (m_currentNoise == 0) { m_currentNoise = TEST_NOISE_TYPES::CELLULAR; } else { m_currentNoise = (TEST_NOISE_TYPES)((int)m_currentNoise - 1); } } if (e.keyCode == VKEY_RIGHT) { if (m_currentNoise == TEST_NOISE_TYPES::CELLULAR) { m_currentNoise = TEST_NOISE_TYPES::SIMPLEX; } else { m_currentNoise = (TEST_NOISE_TYPES)((int)m_currentNoise + 1); } } onNoiseChange(); }); } void TestNoiseScreen::onExit(const vui::GameTime& gameTime VORB_UNUSED) { } void TestNoiseScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { } void TestNoiseScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); int numSamples = m_app->getWindow().getWidth() * (m_app->getWindow().getHeight() - 100); f32v2 destDims = f32v2(m_app->getWindow().getViewportDims()); destDims.y += 100.0f; // Texture m_sb.begin(); m_sb.draw(m_textures[m_currentNoise], f32v2(0.0f, 100.0f), destDims, color::White); m_sb.end(); m_sb.render(f32v2(m_app->getWindow().getViewportDims())); // UI m_sb.begin(); switch (m_currentNoise) { case SIMPLEX: m_sb.drawString(&m_font, "Simplex", f32v2(30.0f), f32v2(0.7f), color::White); break; case CELLULAR: m_sb.drawString(&m_font, "Cellular", f32v2(30.0f), f32v2(0.7f), color::White); break; } char buf[256]; sprintf(buf, "Time %.2lf ms", m_times[m_currentNoise]); m_sb.drawString(&m_font, buf, f32v2(30.0f, 60.0f), f32v2(0.7f), color::White); sprintf(buf, "Samples %d", numSamples); m_sb.drawString(&m_font, buf, f32v2(330.0f, 60.0f), f32v2(0.7f), color::White); sprintf(buf, "Time per sample: %.6lf ms", m_times[m_currentNoise] / numSamples); m_sb.drawString(&m_font, buf, f32v2(630.0f, 60.0f), f32v2(0.7f), color::White); m_sb.end(); m_sb.render(f32v2(m_app->getWindow().getViewportDims())); } void TestNoiseScreen::onNoiseChange() { // Only generate once if (m_textures[m_currentNoise]) return; const f64 frequency = 0.01; int width = m_app->getWindow().getWidth(); int height = m_app->getWindow().getHeight() - 100; std::vector buffer; buffer.resize(width * height); PreciseTimer timer; timer.start(); f64 val; ui8 p; f64v2 cval; switch (m_currentNoise) { case SIMPLEX: for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { val = Noise::raw((f64)x * frequency, (f64)y * frequency, 0.0); p = (ui8)((val + 1.0) * 127.5); buffer[y * width + x].r = p; buffer[y * width + x].g = p; buffer[y * width + x].b = p; buffer[y * width + x].a = 255; } } break; case CELLULAR: for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { cval = Noise::cellular(f64v3((f64)x * frequency, (f64)y * frequency, 0.0)); val = (cval.y - cval.x); p = (ui8)((val + 1.0) * 127.5); buffer[y * width + x].r = p; buffer[y * width + x].g = p; buffer[y * width + x].b = p; buffer[y * width + x].a = 255; } } break; } m_times[m_currentNoise] = timer.stop(); m_textures[m_currentNoise] = vg::GpuMemory::uploadTexture((void*)buffer.data(), width, height); } ================================================ FILE: SoA/TestNoiseScreen.h ================================================ #pragma once #ifndef TestNoiseScreen_h__ #define TestNoiseScreen_h__ #include #include #include #include #include "App.h" const int NUM_TEST_NOISE_TYPES = 2; enum TEST_NOISE_TYPES { SIMPLEX = 0, CELLULAR }; class TestNoiseScreen : public vui::IAppScreen { public: TestNoiseScreen(const App* app); virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: void onNoiseChange(); vg::SpriteBatch m_sb; vg::SpriteFont m_font; TEST_NOISE_TYPES m_currentNoise = SIMPLEX; FpsLimiter m_limiter; AutoDelegatePool m_hooks; VGTexture m_textures[NUM_TEST_NOISE_TYPES]; f64 m_times[NUM_TEST_NOISE_TYPES]; }; #endif ================================================ FILE: SoA/TestPlanetGenScreen.cpp ================================================ #include "stdafx.h" #include "TestPlanetGenScreen.h" #include "soaUtils.h" #include "SpaceSystemAssemblages.h" #include "Errors.h" #include "ShaderLoader.h" #include "SoaEngine.h" #include #include #include #include #include #include #include i32 TestPlanetGenScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestPlanetGenScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestPlanetGenScreen::build() { } void TestPlanetGenScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestPlanetGenScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_F1) { m_terrainRenderer.dispose(); m_terrainRenderer.initGL(); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onWheel, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseWheelEvent& e) { m_eyeDist += -e.dy * 0.025 * m_eyeDist; }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_ESCAPE) { exit(0); } }); glEnable(GL_DEPTH_TEST); glClearColor(1, 0, 0, 1); glClearDepth(1.0); m_terrainRenderer.initGL(); m_atmoRenderer.initGL(); SoaEngine::initState(&m_state); m_eyePos = f32v3(0, 0, m_eyeDist); TerrainPatchMesher::generateIndices(); { // Set up planet SystemOrbitProperties props; PlanetProperties pProps; pProps.diameter = PLANET_RADIUS * 2.0; pProps.mass = 10000.0; PlanetGenLoader loader; loader.init(&m_iom); pProps.planetGenData = loader.loadPlanetGenData("StarSystems/Trinity/Moons/Aldrin/properties.yml"); TerrainFuncProperties tprops; tprops.low = 9; tprops.high = 10; pProps.planetGenData->radius = PLANET_RADIUS; pProps.planetGenData->baseTerrainFuncs.funcs.setData(&tprops, 1); SpaceSystemAssemblages::createPlanet(m_state.spaceSystem, &props, &pProps, &body, m_state.threadPool); } // Set camera properties m_camera.setFieldOfView(90.0f); f32 width = (f32)m_game->getWindow().getWidth(); f32 height = (f32)m_game->getWindow().getHeight(); m_camera.setAspectRatio(width / height); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); } void TestPlanetGenScreen::onExit(const vui::GameTime& gameTime VORB_UNUSED) { } void TestPlanetGenScreen::update(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_eyePos = f64v3(0, 0, PLANET_RADIUS + m_eyeDist + 100.0); m_updater.update(&m_state, m_eyePos, f64v3(0.0)); m_updater.glUpdate(&m_state); } void TestPlanetGenScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_camera.setClippingPlane((f32)(m_eyeDist / 2.0), (f32)(m_eyeDist + PLANET_RADIUS * 10.0)); m_camera.setPosition(f64v3(m_eyePos)); m_camera.update(); f32v3 lightPos = glm::normalize(f32v3(0.0f, 0.0f, 1.0f)); PreciseTimer timer; auto& aCmp = m_state.spaceSystem->atmosphere.getFromEntity(body.entity); auto& arCmp = m_state.spaceSystem->axisRotation.getFromEntity(body.entity); m_terrainRenderer.draw(m_state.spaceSystem->sphericalTerrain.getFromEntity(body.entity), &m_camera, lightPos, f64v3(0.0f/*m_eyePos*/), computeZCoef(m_camera.getFarClip()), &m_slCmp, &arCmp, &aCmp); m_atmoRenderer.draw(m_state.spaceSystem->atmosphere.getFromEntity(body.entity), m_camera.getViewProjectionMatrix(), f32v3(m_eyePos), lightPos, computeZCoef(m_camera.getFarClip()), &m_slCmp); //glFinish(); checkGlError("TestGasGiantScreen::draw"); } ================================================ FILE: SoA/TestPlanetGenScreen.h ================================================ /// /// TestPlanetGenScreen.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 26 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Screen for testing planet generation /// #pragma once #ifndef TestPlanetGenScreen_h__ #define TestPlanetGenScreen_h__ #include "AtmosphereComponentRenderer.h" #include "Camera.h" #include "SpaceSystem.h" #include "SpaceSystemComponents.h" #include "SpaceSystemUpdater.h" #include "SphericalTerrainComponentRenderer.h" #include "SphericalTerrainComponentUpdater.h" #include "PlanetGenLoader.h" #include "SoAState.h" #include #include #include #include #include #include #include class TestPlanetGenScreen : public vui::IGameScreen { public: /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: const f64 PLANET_RADIUS = 6000.0; SphericalTerrainComponentRenderer m_terrainRenderer; AtmosphereComponentRenderer m_atmoRenderer; SystemBody body; f64v3 m_eyePos; f64 m_eyeDist = PLANET_RADIUS; SpaceSystemUpdater m_updater; SpaceLightComponent m_slCmp; Camera m_camera; AutoDelegatePool m_hooks; vio::IOManager m_iom; SoaState m_state; }; #endif // TestPlanetGenScreen_h__ ================================================ FILE: SoA/TestScriptScreen.cpp ================================================ #include "stdafx.h" #include "TestScriptScreen.h" #include #include #include #include "App.h" #include "InputMapper.h" #include "Inputs.h" TestScriptScreen::TestScriptScreen(const App* app, CommonState* state) : IAppScreen(app), m_commonState(state) { // Empty } i32 TestScriptScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestScriptScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestScriptScreen::build() { // Empty } void TestScriptScreen::destroy(const vui::GameTime&) { // Empty } void TestScriptScreen::onEntry(const vui::GameTime&) { m_env.init(); m_env.setNamespaces("onMessage"); m_env.addCDelegate("subscribe", makeFunctor([&](nString name) { onMessage.add(m_env.template getScriptDelegate(name), true); })); m_env.addCDelegate("unsubscribe", makeFunctor([&](nString name) { onMessage.remove(m_env.template getScriptDelegate(name, false)); })); m_env.setNamespaces(); m_env.addCDelegate("C_Print", makeDelegate(&TestScriptScreen::printMessage)); m_env.addCDelegate("C_Add", makeDelegate(&TestScriptScreen::add)); m_env.run(vio::Path("test.lua")); auto del = m_env.getScriptDelegate("doPrint"); onMessage("Hello, World!"); onMessage("Hello, World!"); del.invoke(nullptr, "Hello, Waldo!"); m_sb.init(); m_font.init("Fonts/orbitron_bold-webfont.ttf", 32); m_ui.init(this, m_commonState->window, nullptr, &m_font, &m_sb); auto view = m_ui.makeView("TestView", 1); view.viewEnv->getEnv()->addCDelegate("C_Print", makeDelegate(&TestScriptScreen::printMessage)); view.viewEnv->run("ui_test.lua"); } void TestScriptScreen::onExit(const vui::GameTime&) { // Empty } void TestScriptScreen::update(const vui::GameTime& dt) { m_ui.update((f32)dt.elapsed); } void TestScriptScreen::draw(const vui::GameTime&) { glClear(GL_COLOR_BUFFER_BIT); m_ui.draw(); } ================================================ FILE: SoA/TestScriptScreen.h ================================================ #include #include #include #include #include #include #include #include #include "CommonState.h" class InputMapper; class TestScriptScreen : public vui::IAppScreen { public: TestScriptScreen(const App* app, CommonState* state); i32 getNextScreen() const override; i32 getPreviousScreen() const override; void build() override; void destroy(const vui::GameTime&) override; void onEntry(const vui::GameTime&) override; void onExit(const vui::GameTime&) override; void update(const vui::GameTime&) override; void draw(const vui::GameTime&) override; Event onMessage; static void printMessage(nString message) { std::cout << message << std::endl; } static i32 add(i32 a, i32 b) { return a + b; } private: CommonState* m_commonState; vscript::lua::Environment m_env; vui::ScriptedUI m_ui; vg::SpriteBatch m_sb; vg::SpriteFont m_font; }; ================================================ FILE: SoA/TestStarScreen.cpp ================================================ #include "stdafx.h" #include "TestStarScreen.h" #include "soaUtils.h" #include "Errors.h" #include "SoaOptions.h" #include "SoAState.h" #include #include #include #include #include #include #include #include #include #include TestStarScreen::TestStarScreen(const App* app) : IAppScreen(app) { m_modPathResolver.init("Textures/TexturePacks/" + soaOptions.getStringOption("Texture Pack").defaultValue + "/", "Textures/TexturePacks/" + soaOptions.getStringOption("Texture Pack").value + "/"); } i32 TestStarScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestStarScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestStarScreen::build() { } void TestStarScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { } void TestStarScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_starRenderer.init(&m_modPathResolver); m_starRenderer.initGL(); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_F1) { m_starRenderer.disposeShaders(); //m_hdr.reloadShader(); // dis is broked } else if (e.keyCode == VKEY_UP) { m_isUpDown = true; } else if (e.keyCode == VKEY_DOWN) { m_isDownDown = true; } else if (e.keyCode == VKEY_H) { m_isHDR = !m_isHDR; } else if (e.keyCode == VKEY_G) { m_isGlow = !m_isGlow; } else if (e.keyCode == VKEY_1) { m_is1Pressed = true; } else if (e.keyCode == VKEY_2) { m_is2Pressed = true; } else if (e.keyCode == VKEY_3) { m_is3Pressed = true; } else if (e.keyCode == VKEY_4) { m_is4Pressed = true; } }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_UP) { m_isUpDown = false; } else if (e.keyCode == VKEY_DOWN) { m_isDownDown = false; } else if (e.keyCode == VKEY_1) { m_is1Pressed = false; } else if (e.keyCode == VKEY_2) { m_is2Pressed = false; } else if (e.keyCode == VKEY_3) { m_is3Pressed = false; } else if (e.keyCode == VKEY_4) { m_is4Pressed = false; } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onWheel, [&](Sender s VORB_UNUSED, const vui::MouseWheelEvent& e) { m_eyeDist += -e.dy * 0.05 * m_eyeDist; }); glEnable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); glClearDepth(1.0); m_eyePos = f32v3(0, 0, STAR_RADIUS + 100.0 + m_eyeDist); // Set up components m_sCmp.radius = STAR_RADIUS; m_sCmp.temperature = 5778.0; m_sCmp.mass = 6.56172e29; m_spriteBatch.init(); m_spriteFont.init("Fonts/orbitron_black-webfont.ttf", 32); m_hdrFrameBuffer = new vg::GLRenderTarget(m_game->getWindow().getViewportDims()); m_hdrFrameBuffer->init(vg::TextureInternalFormat::RGBA16F, 0).initDepth(); m_quad.init(); // TODO(Ben): BROKEN //m_hdr.init(&m_quad, &m_camera); m_hdr.hook(&m_quad); m_camera.setFieldOfView(90.0f); f32 width = (f32)m_game->getWindow().getWidth(); f32 height = (f32)m_game->getWindow().getHeight(); m_camera.setAspectRatio(width / height); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setUp(f32v3(0.0f, 1.0f, 0.0f)); } void TestStarScreen::onExit(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { delete m_hdrFrameBuffer; } void TestStarScreen::update(const vui::GameTime& gameTime VORB_UNUSED) { m_eyePos = f32v3(0, 0, STAR_RADIUS + 100.0 + m_eyeDist); const float TMP_INC = 25.0; if (m_isDownDown) { m_sCmp.temperature -= TMP_INC; } else if (m_isUpDown) { m_sCmp.temperature += TMP_INC; } // TODO(Ben): Remove //if (m_is1Pressed) graphicsOptions.hdrExposure -= 0.01f; //if (m_is2Pressed) graphicsOptions.hdrExposure += 0.01f; //if (m_is3Pressed) graphicsOptions.gamma -= 0.01f; //if (m_is4Pressed) graphicsOptions.gamma += 0.01f; } void TestStarScreen::draw(const vui::GameTime& gameTime VORB_UNUSED) { if (m_isHDR) m_hdrFrameBuffer->use(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_camera.setClippingPlane((f32)(m_eyeDist / 2.0), (f32)(m_eyeDist + STAR_RADIUS * 10.0)); m_camera.setPosition(f64v3(m_eyePos)); m_camera.update(); // Render the star f32v3 fEyePos(m_eyePos); f32 zCoef = computeZCoef(m_camera.getFarClip()); // TODO(Ben): render star first and figure out why depth testing is failing m_starRenderer.drawCorona(m_sCmp, m_camera.getViewProjectionMatrix(), m_camera.getViewMatrix(), fEyePos, zCoef); m_starRenderer.drawStar(m_sCmp, m_camera.getViewProjectionMatrix(), f64q(), fEyePos, zCoef); glBlendFunc(GL_ONE, GL_ONE); if (m_isGlow) m_starRenderer.drawGlow(m_sCmp, m_camera.getViewProjectionMatrix(), m_eyePos, m_camera.getAspectRatio(), m_camera.getDirection(), m_camera.getRight()); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (m_isHDR) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glActiveTexture(GL_TEXTURE0); glBindTexture(m_hdrFrameBuffer->getTextureTarget(), m_hdrFrameBuffer->getTextureID()); m_hdr.render(); } m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if (e.keyCode == VKEY_ESCAPE) { exit(0); } }); checkGlError("TestStarScreen::draw"); // Draw the temperature and HDR char buf[256]; sprintf(buf, "Temperature (K): %.0lf", m_sCmp.temperature); m_spriteBatch.begin(); m_spriteBatch.drawString(&m_spriteFont, buf, f32v2(30.0f, 30.0f), f32v2(1.0f), color::AliceBlue); sprintf(buf, "Distance (KM): %.1lf", m_eyeDist + 100.0); m_spriteBatch.drawString(&m_spriteFont, buf, f32v2(30.0f, 65.0f), f32v2(1.0f), color::AliceBlue); sprintf(buf, "Distance (AU): %.4lf", (m_eyeDist + 100.0) * 0.00000000668458712); m_spriteBatch.drawString(&m_spriteFont, buf, f32v2(30.0f, 100.0f), f32v2(1.0f), color::AliceBlue); if (m_isGlow) { m_spriteBatch.drawString(&m_spriteFont, "Glow: Enabled", f32v2(30.0f, 135.0f), f32v2(1.0f), color::AliceBlue); } else { m_spriteBatch.drawString(&m_spriteFont, "Glow: Disabled", f32v2(30.0f, 135.0f), f32v2(1.0f), color::AliceBlue); } if (m_isHDR) { m_spriteBatch.drawString(&m_spriteFont, "HDR: Enabled", f32v2(30.0f, 170.0f), f32v2(1.0f), color::AliceBlue); // TODO(Ben): Remove // sprintf(buf, " Exposure (1,2): %.1lf", (f64)graphicsOptions.hdrExposure); // m_spriteBatch.drawString(&m_spriteFont, buf, f32v2(30.0f, 205.0f), f32v2(1.0f), color::AliceBlue); // sprintf(buf, " Gamma (3,4): %.1lf", (f64)graphicsOptions.gamma); // m_spriteBatch.drawString(&m_spriteFont, buf, f32v2(30.0f, 240.0f), f32v2(1.0f), color::AliceBlue); } else { m_spriteBatch.drawString(&m_spriteFont, "HDR: Disabled", f32v2(30.0f, 170.0f), f32v2(1.0f), color::AliceBlue); } m_spriteBatch.end(); f32 width = (f32)m_game->getWindow().getWidth(); f32 height = (f32)m_game->getWindow().getHeight(); m_spriteBatch.render(f32v2(width, height)); vg::DepthState::FULL.set(); vg::RasterizerState::CULL_CLOCKWISE.set(); } ================================================ FILE: SoA/TestStarScreen.h ================================================ /// /// TestStarScreen.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 9 Apr 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Test screen for star renderer /// #pragma once #ifndef TestStarScreen_h__ #define TestStarScreen_h__ #include "StarComponentRenderer.h" #include "SpaceSystemComponents.h" #include "Camera.h" #include "HdrRenderStage.h" #include "ModPathResolver.h" #include #include #include #include #include #include #include class HdrRenderStage; struct SoaState; class App; class TestStarScreen : public vui::IAppScreen { public: TestStarScreen(const App* app); virtual ~TestStarScreen(){}; /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: const f64 STAR_RADIUS = 696000; StarComponentRenderer m_starRenderer; f64v3 m_eyePos; f64 m_eyeDist = STAR_RADIUS; StarComponent m_sCmp; bool m_isUpDown = false; bool m_isDownDown = false; AutoDelegatePool m_hooks; vg::SpriteBatch m_spriteBatch; vg::SpriteFont m_spriteFont; HdrRenderStage m_hdr; vg::FullQuadVBO m_quad; Camera m_camera; vg::GLRenderTarget* m_hdrFrameBuffer = nullptr; ModPathResolver m_modPathResolver; bool m_isHDR = true; bool m_isGlow = true; bool m_is1Pressed = false; bool m_is2Pressed = false; bool m_is3Pressed = false; bool m_is4Pressed = false; }; #endif // TestStarScreen_h__ ================================================ FILE: SoA/TestUIScreen.cpp ================================================ #include "stdafx.h" #include "TestUIScreen.h" #include #include "App.h" #include "InputMapper.h" #include "Inputs.h" TestUIScreen::TestUIScreen(const App* app, CommonState* state) : IAppScreen(app), m_commonState(state) { // Empty } i32 TestUIScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestUIScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestUIScreen::build() { // Empty } void TestUIScreen::destroy(const vui::GameTime&) { // Empty } void TestUIScreen::onEntry(const vui::GameTime&) { vui::GameWindow* window = m_commonState->window; m_viewport = vui::Viewport(window); m_sb.init(); m_font.init("Fonts/orbitron_bold-webfont.ttf", 32); m_viewport.init("TestUIScreen", { 0.0f, 0.0f, { vui::DimensionType::PIXEL, vui::DimensionType::PIXEL } }, { 1.0f, 1.0f, { vui::DimensionType::WINDOW_WIDTH_PERCENTAGE, vui::DimensionType::WINDOW_HEIGHT_PERCENTAGE } }, &m_font, &m_sb); m_panels[0].init("panel1", f32v4(0.0f)); m_panels[1].init("panel2", f32v4(0.0f)); m_panels[2].init("panel3", f32v4(0.0f)); m_panels[3].init("panel4", f32v4(0.0f)); m_panels[4].init("panel5", f32v4(0.0f)); m_panels[0].setColor(color4{ 255, 0, 0 }); m_panels[0].setHoverColor(color4{ 0, 255, 0 }); m_panels[1].setColor(color4{ 0, 0, 255 }); m_panels[1].setHoverColor(color4{ 255, 255, 0 }); m_panels[2].setColor(color4{ 255, 0, 255 }); m_panels[2].setHoverColor(color4{ 0, 255, 255 }); m_panels[3].setColor(color4{ 0, 0, 0 }); m_panels[3].setHoverColor(color4{ 255, 255, 255 }); m_panels[4].setColor(color4{ 123, 34, 235 }); m_panels[4].setHoverColor(color4{ 234, 100, 0 }); m_panels[0].setZIndex(1); m_panels[1].setZIndex(2); m_panels[2].setZIndex(3); m_panels[3].setZIndex(4); m_panels[4].setZIndex(5); m_panels[0].setAutoScroll(true); m_panels[1].setAutoScroll(true); m_panels[2].setAutoScroll(true); m_panels[3].setAutoScroll(true); m_panels[4].setAutoScroll(true); m_panels[0].setDockState(vui::DockState::BOTTOM); m_panels[1].setDockState(vui::DockState::LEFT); m_panels[2].setDockState(vui::DockState::LEFT); m_panels[3].setDockState(vui::DockState::TOP); m_panels[4].setDockState(vui::DockState::FILL); m_panels[0].setRawDockSize({ 0.25f, { vui::DimensionType::VIEWPORT_HEIGHT_PERCENTAGE } }); m_panels[1].setRawDockSize({ 0.2f, { vui::DimensionType::VIEWPORT_WIDTH_PERCENTAGE } }); m_panels[2].setRawDockSize({ 0.2f, { vui::DimensionType::VIEWPORT_WIDTH_PERCENTAGE } }); m_panels[3].setRawDockSize({ 0.25f, { vui::DimensionType::VIEWPORT_HEIGHT_PERCENTAGE } }); m_checkBox.init("CheckBox", f32v4(30.0f, 30.0f, 150.0f, 30.0f)); m_checkBox.setPadding(f32v4(10.0f, 5.0f, 10.0f, 5.0f)); m_checkBox.setText("Hello, World!"); m_checkBox.setTextScale(f32v2(0.65f)); m_checkBox.setTextAlign(vg::TextAlign::CENTER); m_checkBox.setClipping(vui::Clipping{ vui::ClippingState::HIDDEN, vui::ClippingState::HIDDEN, vui::ClippingState::HIDDEN, vui::ClippingState::HIDDEN }); m_panels[0].addWidget(&m_checkBox); m_label.init("Label", f32v4(130.0f, 130.0f, 120.0f, 30.0f)); m_label.setPadding(f32v4(10.0f, 5.0f, 10.0f, 5.0f)); m_label.setText("Wooooo!"); m_label.setTextScale(f32v2(0.65f)); m_label.setTextAlign(vg::TextAlign::CENTER); m_panels[1].addWidget(&m_label); m_button.init("Button", f32v4(60.0f, 130.0f, 120.0f, 30.0f)); m_button.setPadding(f32v4(10.0f, 5.0f, 10.0f, 5.0f)); m_button.setText("Click Me!"); m_button.setTextScale(f32v2(0.65f)); m_button.setTextAlign(vg::TextAlign::CENTER); m_panels[2].addWidget(&m_button); m_comboBox.init("ComboBox", f32v4(60.0f, 130.0f, 170.0f, 30.0f)); m_comboBox.addItem("This is One."); m_comboBox.addItem("This is Two."); m_comboBox.addItem("This is Three."); m_comboBox.addItem("This is Four."); // TODO(Matthew): Padding not supported by combobox yet, I think - too distracted to check, just occurred to me. // m_comboBox.setPadding(f32v4(10.0f, 5.0f, 10.0f, 5.0f)); m_comboBox.setText("Click Me!"); m_comboBox.setTextScale(f32v2(0.65f)); m_comboBox.setTextAlign(vg::TextAlign::CENTER); m_comboBox.setBackColor(color::Aquamarine); m_comboBox.setBackHoverColor(color::Azure); m_comboBox.setZIndex(2); m_comboBox.setMaxDropHeight(90.0f); m_comboBox.selectItem(0); m_panels[4].addWidget(&m_comboBox); m_widgetList.init("WidgetList", f32v4(50.0f, 50.0f, 200.0f, 200.0f)); m_widgetList.setBackColor(color::Bisque); m_widgetList.setBackHoverColor(color::Crimson); m_widgetList.setAutoScroll(true); m_widgetList.setSpacing(0.0f); m_widgetList.setMaxHeight(200.0f); size_t i = 0; for (auto& button : m_listButtons) { button.init("ListButton" + std::to_string(i++), f32v4(0.0f, 0.0f, 200.0f, 50.0f)); button.setBackColor(color4(40.0f * (f32)i, 20.0f, 20.0f)); button.setBackHoverColor(color4(20.0f, 40.0f * (f32)i, 20.0f)); button.setText("ListButton" + std::to_string(i)); button.setTextScale(f32v2(0.65f)); button.setTextAlign(vg::TextAlign::CENTER); m_widgetList.addItem(&button); } m_panels[3].addWidget(&m_widgetList); m_viewport.addWidget(&m_panels[0]); m_viewport.addWidget(&m_panels[1]); m_viewport.addWidget(&m_panels[2]); m_viewport.addWidget(&m_panels[3]); m_viewport.addWidget(&m_panels[4]); m_viewport.enable(); } void TestUIScreen::onExit(const vui::GameTime&) { m_viewport.dispose(); } void TestUIScreen::update(const vui::GameTime&) { m_viewport.update(); } void TestUIScreen::draw(const vui::GameTime&) { glClear(GL_COLOR_BUFFER_BIT); m_viewport.draw(); } ================================================ FILE: SoA/TestUIScreen.h ================================================ #include #include #include #include #include #include #include #include #include #include #include #include "CommonState.h" class InputMapper; class TestUIScreen : public vui::IAppScreen { public: TestUIScreen(const App* app, CommonState* state); i32 getNextScreen() const override; i32 getPreviousScreen() const override; void build() override; void destroy(const vui::GameTime&) override; void onEntry(const vui::GameTime&) override; void onExit(const vui::GameTime&) override; void update(const vui::GameTime&) override; void draw(const vui::GameTime&) override; private: CommonState* m_commonState; vg::SpriteBatch m_sb; vg::SpriteFont m_font; vui::Viewport m_viewport; vui::Panel m_panels[5]; vui::CheckBox m_checkBox; vui::Label m_label; vui::Button m_button; vui::ComboBox m_comboBox; vui::WidgetList m_widgetList; vui::Button m_listButtons[6]; }; ================================================ FILE: SoA/TestVoxelModelScreen.cpp ================================================ #include "stdafx.h" #include "TestVoxelModelScreen.h" #include #include #include #include #include #include "DebugRenderer.h" #include "Errors.h" #include "ModelMesher.h" #include "VoxelModelLoader.h" #include "soaUtils.h" TestVoxelModelScreen::TestVoxelModelScreen(const App* app) : IAppScreen(app) { // Empty } i32 TestVoxelModelScreen::getNextScreen() const { return SCREEN_INDEX_NO_SCREEN; } i32 TestVoxelModelScreen::getPreviousScreen() const { return SCREEN_INDEX_NO_SCREEN; } void TestVoxelModelScreen::build() { // Empty } void TestVoxelModelScreen::destroy(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestVoxelModelScreen::onEntry(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { if(e.keyCode==VKEY_ESCAPE) { exit(0); } }); m_sb.init(); m_sf.init("Fonts/orbitron_bold-webfont.ttf", 32); m_camera.init(m_game->getWindow().getAspectRatio()); m_camera.setPosition(f64v3(0, 0, 100)); m_camera.setClippingPlane(0.01f, 100000.0f); m_camera.setDirection(f32v3(0.0f, 0.0f, -1.0f)); m_camera.setRight(f32v3(1.0f, 0.0f, 0.0f)); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onMotion, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseMotionEvent& e) { if (m_mouseButtons[0]) { m_camera.rotateFromMouse(-e.dx, -e.dy, 0.1f); } if (m_mouseButtons[1]) { m_camera.rollFromMouse((f32)e.dx, 0.1f); } }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_mouseButtons[0] = true; if (e.button == vui::MouseButton::RIGHT) m_mouseButtons[1] = true; }); m_hooks.addAutoHook(vui::InputDispatcher::mouse.onButtonUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::MouseButtonEvent& e) { if (e.button == vui::MouseButton::LEFT) m_mouseButtons[0] = false; if (e.button == vui::MouseButton::RIGHT) m_mouseButtons[1] = false; }); m_movingForward = false; m_movingBack = false; m_movingLeft = false; m_movingRight = false; m_movingUp = false; m_movingDown = false; m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch(e.keyCode) { case VKEY_W: m_movingForward = true; break; case VKEY_S: m_movingBack = true; break; case VKEY_A: m_movingLeft = true; break; case VKEY_D: m_movingRight = true; break; case VKEY_SPACE: m_movingUp = true; break; case VKEY_LSHIFT: m_movingFast = true; break; case VKEY_M: m_wireFrame = !m_wireFrame; break; case VKEY_LEFT: if (m_currentMesh == 0) { m_currentMesh = m_meshes.size() - 1; } else { m_currentMesh--; } break; case VKEY_RIGHT: m_currentMesh++; if (m_currentMesh >= m_meshes.size()) m_currentMesh = 0; break; case VKEY_F1: // Reload shader m_renderer.dispose(); m_renderer.initGL(); break; } }); m_hooks.addAutoHook(vui::InputDispatcher::key.onKeyUp, [&](Sender s VORB_MAYBE_UNUSED, const vui::KeyEvent& e) { switch(e.keyCode) { case VKEY_W: m_movingForward = false; break; case VKEY_S: m_movingBack = false; break; case VKEY_A: m_movingLeft = false; break; case VKEY_D: m_movingRight = false; break; case VKEY_SPACE: m_movingUp = false; break; case VKEY_LSHIFT: m_movingFast = false; break; } }); m_currentMesh = 0; /************************************************************************/ /* Mesh Creation */ /************************************************************************/ { // Female model nString path = "Models/human_female.qb"; VoxelModel* model = new VoxelModel(); model->loadFromFile(path); addMesh(path, VoxelMeshType::BASIC, model); addMesh(path, VoxelMeshType::MARCHING_CUBES, model); // You can add DUAL_COUNTOURING too, but beware: The implementation is very slow. // addMesh("Models/human_female.qb", VoxelMeshType::DUAL_CONTOURING, model); } { // Male model nString path = "Models/human_male.qb"; VoxelModel* model = new VoxelModel; model->loadFromFile(path); addMesh(path, VoxelMeshType::BASIC, model); addMesh(path, VoxelMeshType::MARCHING_CUBES, model); // You can add DUAL_COUNTOURING too, but beware: The implementation is very slow. // addMesh("Models/human_female.qb", VoxelMeshType::DUAL_CONTOURING, model); } /************************************************************************/ /* */ /************************************************************************/ // init GL m_renderer.initGL(); m_mouseButtons[0] = false; m_mouseButtons[1] = false; m_mouseButtons[2] = false; // Set clear state glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0); } void TestVoxelModelScreen::onExit(const vui::GameTime& gameTime VORB_UNUSED) { // Empty } void TestVoxelModelScreen::update(const vui::GameTime& gameTime) { f32 speed = 5.0f; if (m_movingFast) speed *= 5.0f; if(m_movingForward) { f32v3 offset = m_camera.getDirection() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if(m_movingBack) { f32v3 offset = m_camera.getDirection() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if(m_movingLeft) { f32v3 offset = m_camera.getRight() * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if(m_movingRight) { f32v3 offset = m_camera.getRight() * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if(m_movingUp) { f32v3 offset = f32v3(0, 1, 0) * speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } if(m_movingDown) { f32v3 offset = f32v3(0, 1, 0) * -speed * (f32)gameTime.elapsed; m_camera.offsetPosition(offset); } m_camera.update(); } void TestVoxelModelScreen::draw(const vui::GameTime& gameTime VORB_MAYBE_UNUSED) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); vg::DepthState::FULL.set(); // The winding is backwards for some reason on dual contouring... if (m_meshInfos[m_currentMesh].meshType == VoxelMeshType::DUAL_CONTOURING) { vg::RasterizerState::CULL_COUNTER_CLOCKWISE.set(); } else { vg::RasterizerState::CULL_CLOCKWISE.set(); } // Draw the mesh if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); m_renderer.draw(&m_meshes[m_currentMesh], m_camera.getViewProjectionMatrix(), m_camera.getPosition(), f64q()); if (m_wireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); m_sb.begin(); char buf[512]; // Get a string name nString typeString = "UNKNOWN"; switch (m_meshInfos[m_currentMesh].meshType) { case VoxelMeshType::BASIC: typeString = "Basic"; break; case VoxelMeshType::MARCHING_CUBES: typeString = "Marching Cubes"; break; case VoxelMeshType::DUAL_CONTOURING: typeString = "Dual Contouring"; break; } // Build the string to draw sprintf(buf, "Name: %s\nType: %s\nTriangles: %d\nBuild Time: %.4lf ms\nsize: %f mb", m_meshInfos[m_currentMesh].name.c_str(), typeString.c_str(), m_meshInfos[m_currentMesh].numPolygons, m_meshInfos[m_currentMesh].buildTime, m_meshInfos[m_currentMesh].size / 1000.0f / 1000.0f); // Draw the string m_sb.drawString(&m_sf, buf, f32v2(30.0f), f32v2(1.0f), color::White); // Flush to screen m_sb.end(); m_sb.render(f32v2(m_game->getWindow().getViewportDims())); checkGlError("TestVoxelModelScreen::draw"); } void TestVoxelModelScreen::addMesh(const nString& name, VoxelMeshType meshType, VoxelModel* model) { MeshDebugInfo info; info.name = name; info.meshType = meshType; info.model = model; info.size = model->getMatrix().size.x * model->getMatrix().size.y * model->getMatrix().size.z * sizeof(color4); PreciseTimer timer; timer.start(); // TODO(Ben): Move the switch inside ModelMesher switch (meshType) { case VoxelMeshType::BASIC: m_meshes.push_back(ModelMesher::createMesh(model)); break; case VoxelMeshType::MARCHING_CUBES: m_meshes.push_back(ModelMesher::createMarchingCubesMesh(model)); break; case VoxelMeshType::DUAL_CONTOURING: m_meshes.push_back(ModelMesher::createDualContouringMesh(model)); break; } info.buildTime = timer.stop(); info.numPolygons = m_meshes.back().getTriCount(); m_meshInfos.push_back(info); } ================================================ FILE: SoA/TestVoxelModelScreen.h ================================================ /// /// TestBlockViewScreen.h /// Seed of Andromeda /// /// Created by Frank McCoy on 7 April 2015 /// Copyright 2014-2015 Regrowth Studios /// MIT License /// /// Summary: /// Load and display a voxel model in .QB file format /// #pragma once #ifndef TestVoxelModelScreen_h__ #define TestVoxelModelScreen_h__ #include #include #include #include #include #include #include "Camera.h" #include "VoxelModel.h" #include "VoxelModelRenderer.h" class App; class VoxelMatrix; class VoxelModelVertex; enum class VoxelMeshType { BASIC, MARCHING_CUBES, DUAL_CONTOURING }; struct MeshDebugInfo { nString name; VoxelMeshType meshType; VoxelModel* model; ui32 numPolygons; f64 buildTime; f32 size; }; class TestVoxelModelScreen : public vui::IAppScreen { public: TestVoxelModelScreen(const App* app); /************************************************************************/ /* IGameScreen functionality */ /************************************************************************/ virtual i32 getNextScreen() const override; virtual i32 getPreviousScreen() const override; virtual void build() override; virtual void destroy(const vui::GameTime& gameTime) override; virtual void onEntry(const vui::GameTime& gameTime) override; virtual void onExit(const vui::GameTime& gameTime) override; virtual void update(const vui::GameTime& gameTime) override; virtual void draw(const vui::GameTime& gameTime) override; private: void addMesh(const nString& name, VoxelMeshType meshType, VoxelModel* model); Camera m_camera; AutoDelegatePool m_hooks; ///< Input hooks reservoir bool m_mouseButtons[3]; ui32 m_currentMesh = 0; std::vector m_meshes; std::vector m_meshInfos; VoxelModelRenderer m_renderer; bool m_wireFrame = false; vg::SpriteBatch m_sb; vg::SpriteFont m_sf; bool m_movingForward; bool m_movingBack; bool m_movingLeft; bool m_movingRight; bool m_movingUp; bool m_movingDown; bool m_movingFast; }; #endif ================================================ FILE: SoA/Thread.h ================================================ #pragma once #include template class Thread { public: Thread(i32(*func)(T)) : _func(func), _isFinished(false), _exitCode(0), _argCopy({}) { _threadFunction = [] (Thread* thread) { thread->_exitCode = (*thread->_func)(thread->_argCopy); thread->_isFinished = true; }; } Thread() : Thread(nullptr) {} void start(const T& arg) { _argCopy = arg; _thread = std::thread(_threadFunction, this); } void setFunction(i32(*func)(T)) { _func = func; } const bool getIsFinished() const { return _isFinished; } const i32 getErrorCode() const { return _exitCode; } private: void(*_threadFunction)(Thread*); i32(*_func)(T); std::thread _thread; T _argCopy; bool _isFinished; i32 _exitCode; }; typedef i32(*ThreadVoidFunction)(); class ThreadVoid { public: ThreadVoid(ThreadVoidFunction func) : _func(func), _isFinished(false), _exitCode(0) { _threadFunction = [] (ThreadVoid* thread) { thread->_exitCode = thread->_func(); thread->_isFinished = true; }; } ThreadVoid() : ThreadVoid(nullptr) {} void start() { _thread = std::thread(_threadFunction, this); } void setFunction(ThreadVoidFunction func) { _func = func; } const bool getIsFinished() const { return _isFinished; } const i32 getErrorCode() const { return _exitCode; } private: void(*_threadFunction)(ThreadVoid*); ThreadVoidFunction _func; std::thread _thread; bool _isFinished; i32 _exitCode; }; ================================================ FILE: SoA/TransparentVoxelRenderStage.cpp ================================================ #include "stdafx.h" #include "TransparentVoxelRenderStage.h" #include #include "BlockPack.h" #include "BlockTexturePack.h" #include "Camera.h" #include "ChunkMeshManager.h" #include "ChunkRenderer.h" #include "GameRenderParams.h" #include "GeometrySorter.h" #include "Chunk.h" #include "RenderUtils.h" #include "ShaderLoader.h" #include "SoaOptions.h" void TransparentVoxelRenderStage::hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams) { m_renderer = renderer; m_gameRenderParams = gameRenderParams; } void TransparentVoxelRenderStage::render(const Camera* camera VORB_MAYBE_UNUSED) { glDepthMask(GL_FALSE); ChunkMeshManager* cmm = m_gameRenderParams->chunkMeshmanager; const f64v3& position = m_gameRenderParams->chunkCamera->getPosition(); m_renderer->beginTransparent(m_gameRenderParams->blockTexturePack->getAtlasTexture(), m_gameRenderParams->sunlightDirection, m_gameRenderParams->sunlightColor); glDisable(GL_CULL_FACE); // f64v3 cpos; static i32v3 oldPos = i32v3(0); bool sort = false; i32v3 intPosition(fastFloor(position.x), fastFloor(position.y), fastFloor(position.z)); if (oldPos != intPosition) { //sort the geometry sort = true; oldPos = intPosition; } const std::vector & chunkMeshes = cmm->getChunkMeshes(); { std::lock_guard l(cmm->lckActiveChunkMeshes); if (chunkMeshes.empty()) return; for (size_t i = 0; i < chunkMeshes.size(); i++) { ChunkMesh* cm = chunkMeshes[i]; if (sort) cm->needsSort = true; if (cm->inFrustum) { // TODO(Ben): We should probably do this outside of a lock if (cm->needsSort) { cm->needsSort = false; if (cm->transQuadIndices.size() != 0) { GeometrySorter::sortTransparentBlocks(cm, intPosition); //update index data buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->transIndexID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, cm->transQuadIndices.size() * sizeof(ui32), NULL, GL_STATIC_DRAW); void* v = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, cm->transQuadIndices.size() * sizeof(ui32), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); if (v == NULL) pError("Failed to map sorted transparency buffer."); memcpy(v, &(cm->transQuadIndices[0]), cm->transQuadIndices.size() * sizeof(ui32)); glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); } } m_renderer->drawTransparent(cm, position, m_gameRenderParams->chunkCamera->getViewProjectionMatrix()); } } } glEnable(GL_CULL_FACE); m_renderer->end(); glDepthMask(GL_TRUE); } ================================================ FILE: SoA/TransparentVoxelRenderStage.h ================================================ /// /// TransparentVoxelRenderStage.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 1 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// This file provides the implementation of the transparent voxel /// render stage. Transparent voxels have partial transparency, and /// will be sorted and blended. /// #pragma once #ifndef TransparentVoxelRenderStage_h__ #define TransparentVoxelRenderStage_h__ #include "IRenderStage.h" #include class Camera; class ChunkRenderer; class GameRenderParams; class MeshManager; class TransparentVoxelRenderStage : public IRenderStage { public: void hook(ChunkRenderer* renderer, const GameRenderParams* gameRenderParams); /// Draws the render stage virtual void render(const Camera* camera) override; private: ChunkRenderer* m_renderer; const GameRenderParams* m_gameRenderParams = nullptr; ///< Handle to some shared parameters }; #endif // TransparentVoxelRenderStage_h__ ================================================ FILE: SoA/VRayHelper.cpp ================================================ #include "stdafx.h" #include "VRayHelper.h" #include "BlockPack.h" #include "ChunkGrid.h" #include "VoxelRay.h" #include "VoxelSpaceConversions.h" bool solidVoxelPredBlock(const Block& block) { return block.collide == true; } const VoxelRayQuery VRayHelper::getQuery(const f64v3& pos, const f32v3& dir, f64 maxDistance, ChunkGrid& cg, PredBlock f) { // Set the ray coordinates VoxelRay vr(pos, f64v3(dir)); // Create The Query At The Current Position VoxelRayQuery query = {}; query.location = vr.getNextVoxelPosition(); query.distance = vr.getDistanceTraversed(); // A minimum chunk position for determining voxel coords using only positive numbers i32v3 relativeChunkSpot = VoxelSpaceConversions::voxelToChunk(f64v3(pos.x - maxDistance, pos.y - maxDistance, pos.z - maxDistance)) * CHUNK_WIDTH; i32v3 relativeLocation; // Chunk position i32v3 chunkPos; // TODO: Use A Bounding Box Intersection First And Allow Traversal Beginning Outside The Voxel World // Keep track of the previous chunk for locking ChunkHandle chunk; query.chunkID = ChunkID(0xffffffffffffffff); bool locked = false; // Loop Traversal while (query.distance < maxDistance) { chunkPos = VoxelSpaceConversions::voxelToChunk(query.location); relativeLocation = query.location - relativeChunkSpot; ChunkID id(chunkPos); if (id != query.chunkID) { query.chunkID = id; if (chunk.isAquired()) { if (locked) { chunk->dataMutex.unlock(); locked = false; } chunk.release(); } chunk = cg.accessor.acquire(id); if (chunk->isAccessible) { chunk->dataMutex.lock(); locked = true; } } if (locked) { // Calculate Voxel Index query.voxelIndex = (relativeLocation.x & 0x1f) + (relativeLocation.y & 0x1f) * CHUNK_LAYER + (relativeLocation.z & 0x1f) * CHUNK_WIDTH; // Get Block ID query.id = chunk->blocks.get(query.voxelIndex); // Check For The Block ID if (f(cg.blockPack->operator[](query.id))) { if (locked) chunk->dataMutex.unlock(); chunk.release(); return query; } } // Traverse To The Next query.location = vr.getNextVoxelPosition(); query.distance = vr.getDistanceTraversed(); } if (chunk.isAquired()) { if (locked) chunk->dataMutex.unlock(); chunk.release(); } return query; } const VoxelRayFullQuery VRayHelper::getFullQuery(const f64v3& pos, const f32v3& dir, f64 maxDistance, ChunkGrid& cg, PredBlock f) { // First Convert To Voxel Coordinates VoxelRay vr(pos, f64v3(dir)); // Create The Query At The Current Position VoxelRayFullQuery query = {}; query.inner.location = vr.getNextVoxelPosition(); query.inner.distance = vr.getDistanceTraversed(); query.outer.location = query.inner.location; query.outer.distance = query.inner.distance; // A minimum chunk position for determining voxel coords using only positive numbers i32v3 relativeChunkSpot = VoxelSpaceConversions::voxelToChunk(f64v3(pos.x - maxDistance, pos.y - maxDistance, pos.z - maxDistance)) * CHUNK_WIDTH; i32v3 relativeLocation; i32v3 chunkPos; // TODO: Use A Bounding Box Intersection First And Allow Traversal Beginning Outside The Voxel World // Keep track of the previous chunk for locking ChunkHandle chunk; query.inner.chunkID = ChunkID(0xffffffffffffffff); bool locked = false; // Loop Traversal while (query.inner.distance < maxDistance) { chunkPos = VoxelSpaceConversions::voxelToChunk(query.inner.location); ChunkID id(chunkPos); if (id != query.inner.chunkID) { query.inner.chunkID = id; if (chunk.isAquired()) { if (locked) { chunk->dataMutex.unlock(); locked = false; } chunk.release(); } chunk = cg.accessor.acquire(id); if (chunk->isAccessible) { chunk->dataMutex.lock(); locked = true; } } if (locked) { relativeLocation = query.inner.location - relativeChunkSpot; // Calculate Voxel Index query.inner.voxelIndex = (relativeLocation.x & 0x1f) + (relativeLocation.y & 0x1f) * CHUNK_LAYER + (relativeLocation.z & 0x1f) * CHUNK_WIDTH; // Get Block ID query.inner.id = chunk->blocks.get(query.inner.voxelIndex); // Check For The Block ID if (f(cg.blockPack->operator[](query.inner.id))) { if (locked) chunk->dataMutex.unlock(); chunk.release(); return query; } // Refresh Previous Query query.outer = query.inner; } // Traverse To The Next query.inner.location = vr.getNextVoxelPosition(); query.inner.distance = vr.getDistanceTraversed(); } if (chunk.isAquired()) { if (locked) chunk->dataMutex.unlock(); chunk.release(); } return query; } ================================================ FILE: SoA/VRayHelper.h ================================================ #pragma once class ChunkGrid; class Chunk; #include "BlockData.h" // Returns True For Certain Block Type typedef bool(*PredBlock)(const Block& block); extern bool solidVoxelPredBlock(const Block& block); // Queryable Information class VoxelRayQuery { public: // Block ID BlockID id; // Location Of The Picked Block i32v3 location; f64 distance; // Address Information ChunkID chunkID; ui16 voxelIndex; }; class VoxelRayFullQuery { public: // The Place Of The Chosen Block VoxelRayQuery inner; // The Place Before The Chosen Block VoxelRayQuery outer; }; // Utility Methods For Using A Voxel Ray class VRayHelper { public: // Resolve A Simple Voxel Query static const VoxelRayQuery getQuery(const f64v3& pos, const f32v3& dir, f64 maxDistance, ChunkGrid& cm, PredBlock f = &solidVoxelPredBlock); // Resolve A Voxel Query Keeping Previous Query Information static const VoxelRayFullQuery getFullQuery(const f64v3& pos, const f32v3& dir, f64 maxDistance, ChunkGrid& cm, PredBlock f = &solidVoxelPredBlock); }; ================================================ FILE: SoA/Vertex.h ================================================ /// /// Vertex.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 21 Jun 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Vertex definitions for SoA /// #pragma once #ifndef Vertex_h__ #define Vertex_h__ #include "Vorb/types.h" class ColorVertex { public: f32v3 position; ubyte color[4]; }; const ui8 MESH_FLAG_ACTIVE = 0x1; const ui8 MESH_FLAG_MERGE_RIGHT = 0x2; const ui8 MESH_FLAG_MERGE_FRONT = 0x4; // Describes atlas positioning for a BlockVertex struct AtlasTexturePosition { struct { ui8 atlas; ui8 index; } base; struct { ui8 atlas; ui8 index; } overlay; bool operator==(const AtlasTexturePosition& rhs) const { // Assumes 32 bit struct. return *((const ui32*)this) == *((const ui32*)&rhs); } }; static_assert(sizeof(AtlasTexturePosition) == sizeof(ui32), "AtlasTexturePosition compare will fail."); // Size: 32 Bytes struct BlockVertex { BlockVertex() {} //need deconstructor because of the non-trivial union ~BlockVertex() { position.ui8v3::~ui8v3(); } union { struct { ui8 x; ui8 y; ui8 z; }; ui8v3 position; }; ui8 face; ui8v2 tex; ui8 animationLength; ui8 blendMode; AtlasTexturePosition texturePosition; AtlasTexturePosition normTexturePosition; AtlasTexturePosition dispTexturePosition; ui8v2 textureDims; ui8v2 overlayTextureDims; color3 color; ui8 mesherFlags; color3 overlayColor; ui8 padding; // This isn't a full comparison. Its just for greedy mesh comparison so its lightweight. bool operator==(const BlockVertex& rhs) const { return (color == rhs.color && overlayColor == rhs.overlayColor && texturePosition == rhs.texturePosition); } }; static_assert(sizeof(BlockVertex) == 32, "Size of BlockVertex is not 32"); class LiquidVertex { public: // TODO: x and z can be bytes? f32v3 position; //12 ui8 tex[2]; //14 ui8 textureUnit; //15 ui8 textureIndex; //16 ColorRGBA8 color; //20 ColorRGB8 lampColor; //23 ui8 sunlight; //24 }; class PhysicsBlockVertex { public: ui8 position[3]; //3 ui8 blendMode; //4 ui8 tex[2]; //6 ui8 pad1[2]; //8 ui8 textureAtlas; //9 ui8 overlayTextureAtlas; //10 ui8 textureIndex; //11 ui8 overlayTextureIndex; //12 ui8 textureWidth; //13 ui8 textureHeight; //14 ui8 overlayTextureWidth; //15 ui8 overlayTextureHeight; //16 i8 normal[3]; //19 i8 pad2; //20 }; #endif // Vertex_h__ ================================================ FILE: SoA/VirtualKeyKegDef.inl ================================================ KEG_ENUM_DECL(VirtualKey); KEG_ENUM_DEF(VirtualKey, VirtualKey, kt) { using namespace keg; kt.addValue("unknown", VKEY_UNKNOWN); kt.addValue("a", VKEY_A); kt.addValue("b", VKEY_B); kt.addValue("c", VKEY_C); kt.addValue("d", VKEY_D); kt.addValue("e", VKEY_E); kt.addValue("f", VKEY_F); kt.addValue("g", VKEY_G); kt.addValue("h", VKEY_H); kt.addValue("i", VKEY_I); kt.addValue("j", VKEY_J); kt.addValue("k", VKEY_K); kt.addValue("l", VKEY_L); kt.addValue("m", VKEY_M); kt.addValue("n", VKEY_N); kt.addValue("o", VKEY_O); kt.addValue("p", VKEY_P); kt.addValue("q", VKEY_Q); kt.addValue("r", VKEY_R); kt.addValue("s", VKEY_S); kt.addValue("t", VKEY_T); kt.addValue("u", VKEY_U); kt.addValue("v", VKEY_V); kt.addValue("w", VKEY_W); kt.addValue("x", VKEY_X); kt.addValue("y", VKEY_Y); kt.addValue("z", VKEY_Z); kt.addValue("1", VKEY_1); kt.addValue("2", VKEY_2); kt.addValue("3", VKEY_3); kt.addValue("4", VKEY_4); kt.addValue("5", VKEY_5); kt.addValue("6", VKEY_6); kt.addValue("7", VKEY_7); kt.addValue("8", VKEY_8); kt.addValue("9", VKEY_9); kt.addValue("0", VKEY_0); kt.addValue("return", VKEY_RETURN); kt.addValue("escape", VKEY_ESCAPE); kt.addValue("backspace", VKEY_BACKSPACE); kt.addValue("tab", VKEY_TAB); kt.addValue("space", VKEY_SPACE); kt.addValue("-", VKEY_MINUS); kt.addValue("=", VKEY_EQUALS); kt.addValue("[", VKEY_LEFTBRACKET); kt.addValue("]", VKEY_RIGHTBRACKET); kt.addValue("\\", VKEY_BACKSLASH); kt.addValue("?", VKEY_NONUSHASH); kt.addValue(";", VKEY_SEMICOLON); kt.addValue("'", VKEY_APOSTROPHE); kt.addValue("`", VKEY_GRAVE); kt.addValue(",", VKEY_COMMA); kt.addValue(".", VKEY_PERIOD); kt.addValue("/", VKEY_SLASH); kt.addValue("caps_lock", VKEY_CAPSLOCK); kt.addValue("f1", VKEY_F1); kt.addValue("f2", VKEY_F2); kt.addValue("f3", VKEY_F3); kt.addValue("f4", VKEY_F4); kt.addValue("f5", VKEY_F5); kt.addValue("f6", VKEY_F6); kt.addValue("f7", VKEY_F7); kt.addValue("f8", VKEY_F8); kt.addValue("f9", VKEY_F9); kt.addValue("f10", VKEY_F10); kt.addValue("f11", VKEY_F11); kt.addValue("f12", VKEY_F12); kt.addValue("print_screen", VKEY_PRINTSCREEN); kt.addValue("scroll_lock", VKEY_SCROLLLOCK); kt.addValue("pause", VKEY_PAUSE); kt.addValue("insert", VKEY_INSERT); kt.addValue("home", VKEY_HOME); kt.addValue("page_up", VKEY_PAGEUP); kt.addValue("delete", VKEY_DELETE); kt.addValue("end", VKEY_END); kt.addValue("page_down", VKEY_PAGEDOWN); kt.addValue("right", VKEY_RIGHT); kt.addValue("left", VKEY_LEFT); kt.addValue("down", VKEY_DOWN); kt.addValue("up", VKEY_UP); kt.addValue("num_lock_clear", VKEY_NUMLOCKCLEAR); kt.addValue("kp_/", VKEY_KP_DIVIDE); kt.addValue("kp_*", VKEY_KP_MULTIPLY); kt.addValue("kp_-", VKEY_KP_MINUS); kt.addValue("kp_+", VKEY_KP_PLUS); kt.addValue("kp_enter", VKEY_KP_ENTER); kt.addValue("kp_1", VKEY_KP_1); kt.addValue("kp_2", VKEY_KP_2); kt.addValue("kp_3", VKEY_KP_3); kt.addValue("kp_4", VKEY_KP_4); kt.addValue("kp_5", VKEY_KP_5); kt.addValue("kp_6", VKEY_KP_6); kt.addValue("kp_7", VKEY_KP_7); kt.addValue("kp_8", VKEY_KP_8); kt.addValue("kp_9", VKEY_KP_9); kt.addValue("kp_0", VKEY_KP_0); kt.addValue("kp_.", VKEY_KP_PERIOD); kt.addValue("nonushbackslash", VKEY_NONUSBACKSLASH); kt.addValue("application", VKEY_APPLICATION); kt.addValue("^", VKEY_POWER); kt.addValue("=", VKEY_KP_EQUALS); kt.addValue("f13", VKEY_F13); kt.addValue("f14", VKEY_F14); kt.addValue("f15", VKEY_F15); kt.addValue("f16", VKEY_F16); kt.addValue("f17", VKEY_F17); kt.addValue("f18", VKEY_F18); kt.addValue("f19", VKEY_F19); kt.addValue("f20", VKEY_F20); kt.addValue("f21", VKEY_F21); kt.addValue("f22", VKEY_F22); kt.addValue("f23", VKEY_F23); kt.addValue("f24", VKEY_F24); kt.addValue("execute", VKEY_EXECUTE); kt.addValue("help", VKEY_HELP); kt.addValue("menu", VKEY_MENU); kt.addValue("select", VKEY_SELECT); kt.addValue("stop", VKEY_STOP); kt.addValue("again", VKEY_AGAIN); kt.addValue("undo", VKEY_UNDO); kt.addValue("cut", VKEY_CUT); kt.addValue("copy", VKEY_COPY); kt.addValue("paste", VKEY_PASTE); kt.addValue("find", VKEY_FIND); kt.addValue("mute", VKEY_MUTE); kt.addValue("volume_up", VKEY_VOLUMEUP); kt.addValue("volume_down", VKEY_VOLUMEDOWN); kt.addValue(",", VKEY_KP_COMMA); kt.addValue("equals_as_400", VKEY_KP_EQUALSAS400); kt.addValue("international_1", VKEY_INTERNATIONAL1); kt.addValue("international_2", VKEY_INTERNATIONAL2); kt.addValue("international_3", VKEY_INTERNATIONAL3); kt.addValue("international_4", VKEY_INTERNATIONAL4); kt.addValue("international_5", VKEY_INTERNATIONAL5); kt.addValue("international_6", VKEY_INTERNATIONAL6); kt.addValue("international_7", VKEY_INTERNATIONAL7); kt.addValue("international_8", VKEY_INTERNATIONAL8); kt.addValue("international_9", VKEY_INTERNATIONAL9); kt.addValue("lang1", VKEY_LANG1); kt.addValue("lang2", VKEY_LANG2); kt.addValue("lang3", VKEY_LANG3); kt.addValue("lang4", VKEY_LANG4); kt.addValue("lang5", VKEY_LANG5); kt.addValue("lang6", VKEY_LANG6); kt.addValue("lang7", VKEY_LANG7); kt.addValue("lang8", VKEY_LANG8); kt.addValue("lang9", VKEY_LANG9); kt.addValue("alterase", VKEY_ALTERASE); kt.addValue("sysreq", VKEY_SYSREQ); kt.addValue("cancel", VKEY_CANCEL); kt.addValue("clear", VKEY_CLEAR); kt.addValue("prior", VKEY_PRIOR); kt.addValue("return2", VKEY_RETURN2); kt.addValue("separator", VKEY_SEPARATOR); kt.addValue("out", VKEY_OUT); kt.addValue("oper", VKEY_OPER); kt.addValue("clearagain", VKEY_CLEARAGAIN); kt.addValue("crsel", VKEY_CRSEL); kt.addValue("exsel", VKEY_EXSEL); kt.addValue("kp_00", VKEY_KP_00); kt.addValue("kp_000", VKEY_KP_000); kt.addValue("thousand_separator", VKEY_THOUSANDSSEPARATOR); kt.addValue("decimal_separator", VKEY_DECIMALSEPARATOR); kt.addValue("currency_unit", VKEY_CURRENCYUNIT); kt.addValue("currency_subunit", VKEY_CURRENCYSUBUNIT); kt.addValue("kp_(", VKEY_KP_LEFTPAREN); kt.addValue("kp_)", VKEY_KP_RIGHTPAREN); kt.addValue("kp_{", VKEY_KP_LEFTBRACE); kt.addValue("kp_}", VKEY_KP_RIGHTBRACE); kt.addValue("kp_tab", VKEY_KP_TAB); kt.addValue("kp_backspace", VKEY_KP_BACKSPACE); kt.addValue("kp_A", VKEY_KP_A); kt.addValue("kp_B", VKEY_KP_B); kt.addValue("kp_C", VKEY_KP_C); kt.addValue("kp_D", VKEY_KP_D); kt.addValue("kp_E", VKEY_KP_E); kt.addValue("kp_F", VKEY_KP_F); kt.addValue("kp_xor", VKEY_KP_XOR); kt.addValue("^", VKEY_KP_POWER); kt.addValue("%", VKEY_KP_PERCENT); kt.addValue("<", VKEY_KP_LESS); kt.addValue(">", VKEY_KP_GREATER); kt.addValue("&", VKEY_KP_AMPERSAND); kt.addValue("&&", VKEY_KP_DBLAMPERSAND); kt.addValue("|", VKEY_KP_VERTICALBAR); kt.addValue("||", VKEY_KP_DBLVERTICALBAR); kt.addValue(":", VKEY_KP_COLON); kt.addValue("#", VKEY_KP_HASH); kt.addValue("space", VKEY_KP_SPACE); kt.addValue("@", VKEY_KP_AT); kt.addValue("!", VKEY_KP_EXCLAM); kt.addValue("memstore", VKEY_KP_MEMSTORE); kt.addValue("memrecall", VKEY_KP_MEMRECALL); kt.addValue("memclear", VKEY_KP_MEMCLEAR); kt.addValue("memadd", VKEY_KP_MEMADD); kt.addValue("memsubtract", VKEY_KP_MEMSUBTRACT); kt.addValue("memmultiply", VKEY_KP_MEMMULTIPLY); kt.addValue("memdivide", VKEY_KP_MEMDIVIDE); kt.addValue("kp_+-", VKEY_KP_PLUSMINUS); kt.addValue("kp_clear", VKEY_KP_CLEAR); kt.addValue("clear_entry", VKEY_KP_CLEARENTRY); kt.addValue("binary", VKEY_KP_BINARY); kt.addValue("octal", VKEY_KP_OCTAL); kt.addValue("decimal", VKEY_KP_DECIMAL); kt.addValue("hexadecimal", VKEY_KP_HEXADECIMAL); kt.addValue("lctrl", VKEY_LCTRL); kt.addValue("lshift", VKEY_LSHIFT); kt.addValue("lalt", VKEY_LALT); kt.addValue("lgui", VKEY_LGUI); kt.addValue("rctrl", VKEY_RCTRL); kt.addValue("rshift", VKEY_RSHIFT); kt.addValue("ralt", VKEY_RALT); kt.addValue("rgui", VKEY_RGUI); kt.addValue("mode", VKEY_MODE); kt.addValue("audionext", VKEY_AUDIONEXT); kt.addValue("audioprev", VKEY_AUDIOPREV); kt.addValue("audiostop", VKEY_AUDIOSTOP); kt.addValue("audioplay", VKEY_AUDIOPLAY); kt.addValue("audiomute", VKEY_AUDIOMUTE); kt.addValue("mediaselect", VKEY_MEDIASELECT); kt.addValue("www", VKEY_WWW); kt.addValue("mail", VKEY_MAIL); kt.addValue("calculator", VKEY_CALCULATOR); kt.addValue("computer", VKEY_COMPUTER); kt.addValue("ac_search", VKEY_AC_SEARCH); kt.addValue("ac_home", VKEY_AC_HOME); kt.addValue("ac_back", VKEY_AC_BACK); kt.addValue("ac_forward", VKEY_AC_FORWARD); kt.addValue("ac_stop", VKEY_AC_STOP); kt.addValue("ac_refresh", VKEY_AC_REFRESH); kt.addValue("ac_bookmarks", VKEY_AC_BOOKMARKS); kt.addValue("ac_brightness_down", VKEY_BRIGHTNESSDOWN); kt.addValue("ac_brightness_up", VKEY_BRIGHTNESSUP); kt.addValue("ac_display_switch", VKEY_DISPLAYSWITCH); kt.addValue("kb_dillum_toggle", VKEY_KBDILLUMTOGGLE); kt.addValue("kb_dillum_down", VKEY_KBDILLUMDOWN); kt.addValue("kb_dillum_up", VKEY_KBDILLUMUP); kt.addValue("eject", VKEY_EJECT); kt.addValue("sleep", VKEY_SLEEP); kt.addValue("value", VKEY_HIGHEST_VALUE); } ================================================ FILE: SoA/VoxPool.cpp ================================================ #include "stdafx.h" #include "VoxPool.h" #include "CAEngine.h" #include "ChunkMesher.h" #include "VoxelLightEngine.h" WorkerData::~WorkerData() { delete chunkMesher; delete voxelLightEngine; } ================================================ FILE: SoA/VoxPool.h ================================================ /// /// VoxPool.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 7 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// /// #pragma once #ifndef VoxPool_h__ #define VoxPool_h__ #include // Worker data for a threadPool class WorkerData { public: ~WorkerData(); volatile bool waiting; volatile bool stop; // Each thread gets its own generators class ChunkMesher* chunkMesher = nullptr; class TerrainPatchMesher* terrainMesher = nullptr; class FloraGenerator* floraGenerator = nullptr; class VoxelLightEngine* voxelLightEngine = nullptr; }; typedef vcore::ThreadPool VoxPool; #endif // VoxPool_h__ ================================================ FILE: SoA/VoxelBits.h ================================================ /// /// VoxelBits.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 6 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Contains utils for accessing voxel bits /// TODO: Use C++ bit accessors /// #pragma once #ifndef VoxelBits_h__ #define VoxelBits_h__ //For lamp colors. Used to extract color values from the 16 bit color code #define LAMP_RED_MASK 0x7C00 #define LAMP_GREEN_MASK 0x3E0 #define LAMP_BLUE_MASK 0x1f #define LAMP_RED_SHIFT 10 #define LAMP_GREEN_SHIFT 5 #define FLORA_HEIGHT_MASK 0x1f #define FLORA_YPOS_MASK 0x3E0 #define FLORA_YPOS_SHIFT 5 //no blue shift namespace VoxelBits { inline ui16 getFloraHeight(ui16 b) { return b & FLORA_HEIGHT_MASK; } inline ui16 getFloraPosition(ui16 b) { return (b & FLORA_YPOS_MASK) >> FLORA_YPOS_SHIFT; } inline ui16 getLampRed(ui16 b) { return b & LAMP_RED_MASK; } inline ui16 getLampGreen(ui16 b) { return b & LAMP_GREEN_MASK; } inline ui16 getLampBlue(ui16 b) { return b & LAMP_BLUE_MASK; } inline void setFloraHeight(ui16& b, ui16 floraHeight) { b = (b & (~FLORA_HEIGHT_MASK)) | floraHeight; } inline void setFloraPosition(ui16& b, ui16 yPos) { b = (b & (~FLORA_YPOS_MASK)) | (yPos << FLORA_YPOS_SHIFT); } inline ui16 getLampRedFromHex(ui16 color) { return (color & LAMP_RED_MASK) >> LAMP_RED_SHIFT; } inline ui16 getLampGreenFromHex(ui16 color) { return (color & LAMP_GREEN_MASK) >> LAMP_GREEN_SHIFT; } inline ui16 getLampBlueFromHex(ui16 color) { return color & LAMP_BLUE_MASK; } } #endif // VoxelBits_h__ ================================================ FILE: SoA/VoxelCoordinateSpaces.h ================================================ /// /// VoxelCoordinateSpaces.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 27 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Contains the voxel coordinate space definitions. /// #pragma once #ifndef VoxelCoordinateSpaces_h__ #define VoxelCoordinateSpaces_h__ #include "Vorb/types.h" enum WorldCubeFace { FACE_TOP = 0, FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, FACE_BOTTOM, FACE_NONE }; struct ChunkPosition2D { ChunkPosition2D():face(FACE_TOP) {} operator i32v2&() { return pos; } operator const i32v2&() const { return pos; } union { i32v2 pos; UNIONIZE(i32 x; i32 z); }; WorldCubeFace face; }; struct ChunkPosition3D { ChunkPosition3D():face(FACE_TOP) {} operator i32v2() const { return i32v2(pos.x, pos.z); } operator i32v3&() { return pos; } operator const i32v3&() const { return pos; } union { i32v3 pos; UNIONIZE(i32 x; i32 y; i32 z); }; WorldCubeFace face; }; struct VoxelPosition2D { VoxelPosition2D():face(FACE_TOP) {} operator f64v2&() { return pos; } operator const f64v2&() const { return pos; } union { f64v2 pos; UNIONIZE(f64 x; f64 z); }; WorldCubeFace face; }; struct VoxelPosition3D { VoxelPosition3D():face(FACE_TOP) {} operator f64v2() const { return f64v2(pos.x, pos.z); } operator f64v3&() { return pos; } operator const f64v3&() const { return pos; } union { f64v3 pos; UNIONIZE(f64 x; f64 y; f64 z); }; WorldCubeFace face; }; #endif // VoxelCoordinateSpaces_h__ ================================================ FILE: SoA/VoxelEditor.cpp ================================================ #include "stdafx.h" #include "VoxelEditor.h" #include "BlockData.h" #include "Chunk.h" #include "ChunkGrid.h" #include "ChunkUpdater.h" #include "Item.h" #include "VoxelNavigation.inl" #include "VoxelSpaceConversions.h" // TODO: Implement and remove VORB_UNUSED tags. void VoxelEditor::editVoxels(ChunkGrid& grid, ItemStack* block) { if (m_startPosition.x == INT_MAX || m_endPosition.x == INT_MAX) { return; } switch (m_currentTool) { case EDITOR_TOOLS::AABOX: placeAABox(grid, block); break; case EDITOR_TOOLS::LINE: placeLine(grid, block); break; } } void VoxelEditor::placeAABox(ChunkGrid& grid, ItemStack* block) { //BlockID blockID; // int soundNum = 0; int yStart, yEnd; int zStart, zEnd; int xStart, xEnd; i32v3 start = m_startPosition; i32v3 end = m_endPosition; //Set up iteration bounds if (start.y < end.y) { yStart = start.y; yEnd = end.y; } else { yEnd = start.y; yStart = end.y; } if (start.z < end.z) { zStart = start.z; zEnd = end.z; } else { zEnd = start.z; zStart = end.z; } if (start.x < end.x) { xStart = start.x; xEnd = end.x; } else { xEnd = start.x; xStart = end.x; } // Keep track of which chunk is locked Chunk* chunk = nullptr; ChunkID currentID(0xffffffffffffffff); bool locked = false; std::map modifiedChunks; for (int y = yStart; y <= yEnd; y++) { for (int z = zStart; z <= zEnd; z++) { for (int x = xStart; x <= xEnd; x++) { i32v3 chunkPos = VoxelSpaceConversions::voxelToChunk(i32v3(x, y, z)); ChunkID id(chunkPos); if (id != currentID) { currentID = id; if (chunk) { if (locked) { chunk->dataMutex.unlock(); locked = false; } } // Only need to aquire once auto it = modifiedChunks.find(currentID); if (it == modifiedChunks.end()) { chunk = modifiedChunks.insert(std::make_pair(currentID, grid.accessor.acquire(id))).first->second; } else { chunk = it->second; } if (chunk->isAccessible) { chunk->dataMutex.lock(); locked = true; } } if (locked) { i32v3 pos = i32v3(x, y, z) - chunkPos * CHUNK_WIDTH; int voxelIndex = pos.x + pos.y * CHUNK_LAYER + pos.z * CHUNK_WIDTH; // TODO: This needs implementing. //blockID = chunk->blocks.get(voxelIndex); if (!block) { // Breaking blocks } else { // Placing blocks block->count--; // ChunkUpdater::placeBlock(chunk, ) ChunkUpdater::placeBlockNoUpdate(chunk, voxelIndex, block->pack->operator[](block->id).blockID); if (block->count == 0) { if (locked) chunk->dataMutex.unlock(); for (auto& it : modifiedChunks) { if (it.second->isAccessible) { it.second->DataChange(it.second); } it.second.release(); } stopDragging(); return; } } } } } } if (locked) chunk->dataMutex.unlock(); for (auto& it : modifiedChunks) { if (it.second->isAccessible) { it.second->DataChange(it.second); } it.second.release(); } stopDragging(); } void VoxelEditor::stopDragging() { //This means we no longer have a selection box m_startPosition = i32v3(INT_MAX); m_endPosition = i32v3(INT_MAX); } void VoxelEditor::placeLine(ChunkGrid& grid VORB_UNUSED, ItemStack* block VORB_UNUSED) { } bool VoxelEditor::isEditing() { return (m_startPosition.x != INT_MAX && m_endPosition.x != INT_MAX); } void VoxelEditor::drawGuides(vg::GLProgram* program VORB_UNUSED, const f64v3& cameraPos VORB_UNUSED, const f32m4 &VP VORB_UNUSED, int blockID VORB_UNUSED) { switch (m_currentTool) { case EDITOR_TOOLS::AABOX:{ // const float BOX_PAD = 0.001f; i32v3 startPosition; startPosition.x = glm::min(m_startPosition.x, m_endPosition.x); startPosition.y = glm::min(m_startPosition.y, m_endPosition.y); startPosition.z = glm::min(m_startPosition.z, m_endPosition.z); //const i32v3 size = glm::abs(m_endPosition - m_startPosition) + i32v3(1); if (blockID != 0){ // DrawWireBox(program, startPosition.x - BOX_PAD, startPosition.y - BOX_PAD, startPosition.z - BOX_PAD, size.x + BOX_PAD * 2, size.y + BOX_PAD * 2, size.z + BOX_PAD * 2, 2, cameraPos, VP, f32v4(0.0, 0.0, 1.0, 1.0)); // DrawWireBox(program, startPosition.x + BOX_PAD, startPosition.y + BOX_PAD, startPosition.z + BOX_PAD, size.x - BOX_PAD * 2, size.y - BOX_PAD * 2, size.z - BOX_PAD * 2, 2, cameraPos, VP, f32v4(0.0, 0.0, 1.0, 1.0)); } else{ // DrawWireBox(program, startPosition.x - BOX_PAD, startPosition.y - BOX_PAD, startPosition.z - BOX_PAD, size.x + BOX_PAD * 2, size.y + BOX_PAD * 2, size.z + BOX_PAD * 2, 2, cameraPos, VP, f32v4(1.0, 0.0, 0.0, 1.0)); // DrawWireBox(program, startPosition.x + BOX_PAD, startPosition.y + BOX_PAD, startPosition.z + BOX_PAD, size.x - BOX_PAD * 2, size.y - BOX_PAD * 2, size.z - BOX_PAD * 2, 2, cameraPos, VP, f32v4(1.0, 0.0, 0.0, 1.0)); } } break; default: break; } } ================================================ FILE: SoA/VoxelEditor.h ================================================ #pragma once #include #include #include DECL_VG(class GLProgram); class ChunkGrid; class PhysicsEngine; struct ItemStack; class EditorNode { // i32v3 position; }; enum class EDITOR_TOOLS { AABOX, LINE }; class VoxelEditor { public: void editVoxels(ChunkGrid& grid, ItemStack* block); void stopDragging(); void setStartPosition(const i32v3& position) { m_startPosition = position; } void setEndPosition(const i32v3& position) { m_endPosition = position; } void setEditorTool(EDITOR_TOOLS editorTool) { m_currentTool = editorTool; } bool isEditing(); const i32v3& getStartPosition() const { return m_startPosition; } const i32v3& getEndPosition() const { return m_endPosition; } //Draws the guide lines void drawGuides(vg::GLProgram* program, const f64v3& cameraPos, const f32m4 &VP, int blockID); private: void placeAABox(ChunkGrid& grid, ItemStack* block); void placeLine(ChunkGrid& grid, ItemStack* block); // v v v Just some ideas v v v // void placeBox(); // void placeWireBox(); // void placeSlab(); // void placeWireSlab(); i32v3 m_startPosition = i32v3(INT_MAX); i32v3 m_endPosition = i32v3(INT_MAX); std::vector m_currentShape; // bool m_isAxisAligned; EDITOR_TOOLS m_currentTool = EDITOR_TOOLS::AABOX; }; ================================================ FILE: SoA/VoxelLightEngine.cpp ================================================ #include "stdafx.h" #include "VoxelLightEngine.h" #include "Errors.h" #include "VoxelNavigation.inl" // TODO: Do we still want this system as is? If so reimplement and remove VORB_UNUSED tags. void VoxelLightEngine::calculateLight(Chunk* chunk VORB_UNUSED) { ////Flush all edge queues //_lockedChunk = nullptr; ////Sunlight Calculation //if (chunk->sunlightRemovalQueue.size()) { // vvox::swapLockedChunk(chunk, _lockedChunk); // //Removal // while (chunk->sunlightRemovalQueue.size()){ // auto& node = chunk->sunlightRemovalQueue.front(); // removeSunlightBFS(chunk, (int)node.blockIndex, node.oldLightVal); // chunk->sunlightRemovalQueue.pop(); // } // std::queue().swap(chunk->sunlightRemovalQueue); //forces memory to be freed //} //if (chunk->sunlightUpdateQueue.size()) { // vvox::swapLockedChunk(chunk, _lockedChunk); // //Addition // while (chunk->sunlightUpdateQueue.size()){ // auto& node = chunk->sunlightUpdateQueue.front(); // placeSunlightBFS(chunk, (int)node.blockIndex, (int)node.lightVal); // chunk->sunlightUpdateQueue.pop(); // } // std::queue().swap(chunk->sunlightUpdateQueue); //forces memory to be freed //} ////Voxel Light Calculation //if (chunk->lampLightRemovalQueue.size()) { // vvox::swapLockedChunk(chunk, _lockedChunk); // //Removal // while (chunk->lampLightRemovalQueue.size()){ // auto& node = chunk->lampLightRemovalQueue.front(); // removeLampLightBFS(chunk, (int)node.blockIndex, node.oldLightColor); // chunk->lampLightRemovalQueue.pop(); // } // std::queue().swap(chunk->lampLightRemovalQueue); //forces memory to be freed //} //if (chunk->lampLightUpdateQueue.size()) { // vvox::swapLockedChunk(chunk, _lockedChunk); // //Addition // while (chunk->lampLightUpdateQueue.size()) { // auto& node = chunk->lampLightUpdateQueue.front(); // placeLampLightBFS(chunk, (int)node.blockIndex, node.lightColor); // chunk->lampLightUpdateQueue.pop(); // } // std::queue().swap(chunk->lampLightUpdateQueue); //forces memory to be freed //} //if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; //} } void VoxelLightEngine::calculateSunlightExtend(Chunk* chunk VORB_UNUSED) { //int blockIndex; //int y; //_lockedChunk = nullptr; //vvox::swapLockedChunk(chunk, _lockedChunk); //for (ui32 i = 0; i < chunk->sunExtendList.size(); i++){ // blockIndex = chunk->sunExtendList[i]; // if (chunk->getSunlight(blockIndex) == MAXLIGHT){ // y = blockIndex / CHUNK_LAYER; // extendSunRay(chunk, blockIndex - y * CHUNK_LAYER, y); // } //} //std::vector().swap(chunk->sunExtendList); //forces memory to be freed //if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; //} } void VoxelLightEngine::calculateSunlightRemoval(Chunk* chunk VORB_UNUSED) { //int blockIndex; //int y; //_lockedChunk = nullptr; //vvox::swapLockedChunk(chunk, _lockedChunk); //for (ui32 i = 0; i < chunk->sunRemovalList.size(); i++){ // blockIndex = chunk->sunRemovalList[i]; // if (chunk->getSunlight(blockIndex) == 0){ // y = blockIndex / CHUNK_LAYER; // blockSunRay(chunk, blockIndex - y * CHUNK_LAYER, y - 1); // } //} //std::vector().swap(chunk->sunRemovalList); //forces memory to be freed //if (_lockedChunk) { // _lockedChunk->unlock(); // _lockedChunk = nullptr; //} } //Check for sun rays from the top chunk void VoxelLightEngine::checkTopForSunlight(Chunk* chunk VORB_UNUSED) { //int blockIndex; //ui16 topLight; //static ui16 topSunlight[CHUNK_LAYER]; //if (chunk->top && chunk->top->isAccessible) { // // First grab the sunlight from the top // chunk->top->lock(); // for (int i = 0; i < CHUNK_LAYER; i++) { // topSunlight[i] = chunk->top->getSunlight(i); // } // chunk->top->unlock(); // // Now check for sun extend // chunk->lock(); // for (int i = 0; i < CHUNK_LAYER; i++) { // blockIndex = i + 31 * CHUNK_LAYER; // topLight = topSunlight[i]; // if ((chunk->getBlock(blockIndex).blockLight == 0) && topLight == MAXLIGHT) { // chunk->setSunlight(blockIndex, MAXLIGHT); // chunk->sunExtendList.push_back(blockIndex); // } else if (topLight == 0) { //if the top is blocking sunlight // if (chunk->getSunlight(blockIndex) == MAXLIGHT) { // chunk->setSunlight(blockIndex, 0); // chunk->sunRemovalList.push_back(blockIndex); // } // } // } // chunk->unlock(); //} } void VoxelLightEngine::blockSunRay(Chunk* chunk VORB_UNUSED, int xz VORB_UNUSED, int y VORB_UNUSED) { //int i = y; //start at the current block //chunk->changeState(ChunkStates::MESH); //while (true){ //do the light removal iteration // if (i == -1){ //bottom chunk // if (chunk->bottom && chunk->bottom->isAccessible){ // vvox::swapLockedChunk(chunk->bottom, _lockedChunk); // VoxelLightEngine::blockSunRay(chunk->bottom, xz, 31); //continue the algorithm // vvox::swapLockedChunk(chunk, _lockedChunk); // } // return; // } else{ // if (chunk->getSunlight(xz + i*CHUNK_LAYER) == MAXLIGHT){ // chunk->setSunlight(xz + i*CHUNK_LAYER, 0); // VoxelLightEngine::removeSunlightBFS(chunk, xz + i*CHUNK_LAYER, MAXLIGHT); // } else{ // return; // } // i--; // } //} } void VoxelLightEngine::extendSunRay(Chunk* chunk VORB_UNUSED, int xz VORB_UNUSED, int y VORB_UNUSED) { //int i = y; //start at the current block, for extension to other chunks //int blockIndex; //chunk->changeState(ChunkStates::MESH); //while (true){ // if (i == -1){ // if (chunk->bottom && chunk->bottom->isAccessible){ // vvox::swapLockedChunk(chunk->bottom, _lockedChunk); // extendSunRay(chunk->bottom, xz, 31); //continue the algorithm // vvox::swapLockedChunk(chunk, _lockedChunk); // } // return; // } else{ // blockIndex = xz + i*CHUNK_LAYER; // if (chunk->getBlock(blockIndex).blockLight == 0){ // if (chunk->getSunlight(blockIndex) != MAXLIGHT){ // chunk->sunlightUpdateQueue.emplace(blockIndex, MAXLIGHT); // } // // } else{ // return; // } // } // i--; //} } inline void removeSunlightNeighborUpdate(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 light VORB_UNUSED) { /* ui8 lightVal = chunk->getSunlight(blockIndex); if (lightVal > 0){ if (lightVal <= light){ chunk->setSunlight(blockIndex, 0); chunk->sunlightRemovalQueue.emplace(blockIndex, light); } else { chunk->sunlightUpdateQueue.emplace(blockIndex, 0); } }*/ } void VoxelLightEngine::removeSunlightBFS(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui8 oldLightVal VORB_UNUSED) { //ui8 nextIntensity; //if (oldLightVal > 0) { // nextIntensity = oldLightVal - 1; //} else { // nextIntensity = 0; //} //int x = blockIndex % CHUNK_WIDTH; //int y = blockIndex / CHUNK_LAYER; //int z = (blockIndex % CHUNK_LAYER) / CHUNK_WIDTH; //Chunk*& left = chunk->left; //Chunk*& right = chunk->right; //Chunk*& back = chunk->back; //Chunk*& front = chunk->front; //Chunk*& top = chunk->top; //Chunk*& bottom = chunk->bottom; //if (x > 0){ //left // removeSunlightNeighborUpdate(chunk, blockIndex - 1, nextIntensity); //} else if (left && left->isAccessible){ // vvox::swapLockedChunk(left, _lockedChunk); // removeSunlightNeighborUpdate(left, blockIndex + CHUNK_WIDTH - 1, nextIntensity); // left->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (x < CHUNK_WIDTH - 1){ //right // removeSunlightNeighborUpdate(chunk, blockIndex + 1, nextIntensity); //} else if (right && right->isAccessible){ // vvox::swapLockedChunk(right, _lockedChunk); // removeSunlightNeighborUpdate(right, blockIndex - CHUNK_WIDTH + 1, nextIntensity); // right->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (z > 0){ //back // removeSunlightNeighborUpdate(chunk, blockIndex - CHUNK_WIDTH, nextIntensity); //} else if (back && back->isAccessible){ // vvox::swapLockedChunk(back, _lockedChunk); // removeSunlightNeighborUpdate(back, blockIndex + CHUNK_LAYER - CHUNK_WIDTH, nextIntensity); // back->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (z < CHUNK_WIDTH - 1){ //front // removeSunlightNeighborUpdate(chunk, blockIndex + CHUNK_WIDTH, nextIntensity); //} else if (front && front->isAccessible){ // vvox::swapLockedChunk(front, _lockedChunk); // removeSunlightNeighborUpdate(front, blockIndex - CHUNK_LAYER + CHUNK_WIDTH, nextIntensity); // front->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (y > 0){ //bottom // removeSunlightNeighborUpdate(chunk, blockIndex - CHUNK_LAYER, nextIntensity); //} else if (bottom && bottom->isAccessible){ // vvox::swapLockedChunk(bottom, _lockedChunk); // removeSunlightNeighborUpdate(bottom, CHUNK_SIZE - CHUNK_LAYER + blockIndex, nextIntensity); // bottom->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (y < CHUNK_WIDTH - 1){ //top // removeSunlightNeighborUpdate(chunk, blockIndex + CHUNK_LAYER, nextIntensity); //} else if (top && top->isAccessible){ // vvox::swapLockedChunk(top, _lockedChunk); // removeSunlightNeighborUpdate(top, blockIndex - CHUNK_SIZE + CHUNK_LAYER, nextIntensity); // top->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //chunk->changeState(ChunkStates::MESH); } inline void placeSunlightNeighborUpdate(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 light VORB_UNUSED) { //if (chunk->getSunlight(blockIndex) < light){ // if (chunk->getBlock(blockIndex).allowLight){ // chunk->setSunlight(blockIndex, (ui8)light); // TODO(Ben) Wrong type? // chunk->sunlightUpdateQueue.emplace(blockIndex, light); // } //} } void VoxelLightEngine::placeSunlightBFS(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui8 intensity VORB_UNUSED) { //if (intensity > chunk->getSunlight(blockIndex)) { // //Set the light value // chunk->setSunlight(blockIndex, intensity); //} else { // //If intensity is less that the actual light value, use the actual // intensity = chunk->getSunlight(blockIndex); //} //if (intensity <= 1) return; ////Reduce by 1 to prevent a bunch of -1 //ui8 newIntensity = (intensity - 1); //chunk->dirty = 1; //int x = blockIndex % CHUNK_WIDTH; //int y = blockIndex / CHUNK_LAYER; //int z = (blockIndex % CHUNK_LAYER) / CHUNK_WIDTH; //Chunk*& left = chunk->left; //Chunk*& right = chunk->right; //Chunk*& back = chunk->back; //Chunk*& front = chunk->front; //Chunk*& top = chunk->top; //Chunk*& bottom = chunk->bottom; //if (x > 0){ //left // placeSunlightNeighborUpdate(chunk, blockIndex - 1, newIntensity); //} else if (left && left->isAccessible){ // vvox::swapLockedChunk(left, _lockedChunk); // placeSunlightNeighborUpdate(left, blockIndex + CHUNK_WIDTH - 1, newIntensity); // left->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (x < CHUNK_WIDTH - 1){ //right // placeSunlightNeighborUpdate(chunk, blockIndex + 1, newIntensity); //} else if (right && right->isAccessible){ // vvox::swapLockedChunk(right, _lockedChunk); // placeSunlightNeighborUpdate(right, blockIndex - CHUNK_WIDTH + 1, newIntensity); // right->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (z > 0){ //back // placeSunlightNeighborUpdate(chunk, blockIndex - CHUNK_WIDTH, newIntensity); //} else if (back && back->isAccessible){ // vvox::swapLockedChunk(back, _lockedChunk); // placeSunlightNeighborUpdate(back, blockIndex + CHUNK_LAYER - CHUNK_WIDTH, newIntensity); // back->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (z < CHUNK_WIDTH - 1){ //front // placeSunlightNeighborUpdate(chunk, blockIndex + CHUNK_WIDTH, newIntensity); //} else if (front && front->isAccessible){ // vvox::swapLockedChunk(front, _lockedChunk); // placeSunlightNeighborUpdate(front, blockIndex - CHUNK_LAYER + CHUNK_WIDTH, newIntensity); // front->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (y > 0){ //bottom // placeSunlightNeighborUpdate(chunk, blockIndex - CHUNK_LAYER, newIntensity); //} else if (bottom && bottom->isAccessible){ // vvox::swapLockedChunk(bottom, _lockedChunk); // placeSunlightNeighborUpdate(bottom, CHUNK_SIZE - CHUNK_LAYER + blockIndex, newIntensity); // bottom->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //if (y < CHUNK_WIDTH - 1){ //top // placeSunlightNeighborUpdate(chunk, blockIndex + CHUNK_LAYER, newIntensity); //} else if (top && top->isAccessible){ // vvox::swapLockedChunk(top, _lockedChunk); // placeSunlightNeighborUpdate(top, blockIndex - CHUNK_SIZE + CHUNK_LAYER, newIntensity); // top->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); //} //chunk->changeState(ChunkStates::MESH); } inline ui16 getMaxLampColors(const ui16 redA VORB_UNUSED, const ui16 greenA VORB_UNUSED, const ui16 blueA VORB_UNUSED, const ui16 b VORB_UNUSED) { /* ui16 redB = b & LAMP_RED_MASK; ui16 greenB = b & LAMP_GREEN_MASK; ui16 blueB = b & LAMP_BLUE_MASK; return MAX(redA, redB) | MAX(greenA, greenB) | MAX(blueA, blueB);*/ return 0; } inline void getLampColors(const ui16 l VORB_UNUSED, ui16 &r VORB_UNUSED, ui16 &g VORB_UNUSED, ui16 &b VORB_UNUSED) { /* r = l & LAMP_RED_MASK; g = l & LAMP_GREEN_MASK; b = l & LAMP_BLUE_MASK;*/ } inline void removeLampNeighborUpdate(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 intensityRed VORB_UNUSED, ui16 intensityGreen VORB_UNUSED, ui16 intensityBlue VORB_UNUSED, ui16 light VORB_UNUSED) { /* ui16 nextRed, nextGreen, nextBlue; ui16 nextLight = chunk->getLampLight(blockIndex); getLampColors(nextLight, nextRed, nextGreen, nextBlue); if ((nextRed && nextRed <= intensityRed) || (nextGreen && nextGreen <= intensityGreen) || (nextBlue && nextBlue <= intensityBlue)){ chunk->setLampLight(blockIndex, 0); chunk->lampLightRemovalQueue.emplace(blockIndex, light); if (nextRed > intensityRed || nextGreen > intensityGreen || nextBlue > intensityBlue){ chunk->lampLightUpdateQueue.emplace(blockIndex, 0); } } else if (nextLight > 0) { chunk->lampLightUpdateQueue.emplace(blockIndex, 0); }*/ } void VoxelLightEngine::removeLampLightBFS(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 light VORB_UNUSED) { //#define RED1 0x400 //#define GREEN1 0x20 //#define BLUE1 1 // // ui16 intensityRed = light & LAMP_RED_MASK; // ui16 intensityGreen = light & LAMP_GREEN_MASK; // ui16 intensityBlue = light & LAMP_BLUE_MASK; // // //Reduce by 1 // if (intensityRed != 0) { // intensityRed -= RED1; // light -= RED1; // } // if (intensityGreen != 0) { // intensityGreen -= GREEN1; // light -= GREEN1; // } // if (intensityBlue != 0) { // intensityBlue -= BLUE1; // light -= BLUE1; // } // // int x = blockIndex % CHUNK_WIDTH; // int y = blockIndex / CHUNK_LAYER; // int z = (blockIndex % CHUNK_LAYER) / CHUNK_WIDTH; // // Chunk*& left = chunk->left; // Chunk*& right = chunk->right; // Chunk*& back = chunk->back; // Chunk*& front = chunk->front; // Chunk*& top = chunk->top; // Chunk*& bottom = chunk->bottom; // // if (x > 0){ //left // removeLampNeighborUpdate(chunk, blockIndex - 1, intensityRed, intensityGreen, intensityBlue, light); // } else if (left && left->isAccessible){ // vvox::swapLockedChunk(left, _lockedChunk); // removeLampNeighborUpdate(left, blockIndex + CHUNK_WIDTH - 1, intensityRed, intensityGreen, intensityBlue, light); // left->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (x < CHUNK_WIDTH - 1){ //right // removeLampNeighborUpdate(chunk, blockIndex + 1, intensityRed, intensityGreen, intensityBlue, light); // } else if (right && right->isAccessible){ // vvox::swapLockedChunk(right, _lockedChunk); // removeLampNeighborUpdate(right, blockIndex - CHUNK_WIDTH + 1, intensityRed, intensityGreen, intensityBlue, light); // right->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (z > 0){ //back // removeLampNeighborUpdate(chunk, blockIndex - CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue, light); // } else if (back && back->isAccessible){ // vvox::swapLockedChunk(back, _lockedChunk); // removeLampNeighborUpdate(back, blockIndex + CHUNK_LAYER - CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue, light); // back->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (z < CHUNK_WIDTH - 1){ //front // removeLampNeighborUpdate(chunk, blockIndex + CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue, light); // } else if (front && front->isAccessible){ // vvox::swapLockedChunk(front, _lockedChunk); // removeLampNeighborUpdate(front, blockIndex - CHUNK_LAYER + CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue, light); // front->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (y > 0){ //bottom // removeLampNeighborUpdate(chunk, blockIndex - CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue, light); // } else if (bottom && bottom->isAccessible){ // vvox::swapLockedChunk(bottom, _lockedChunk); // removeLampNeighborUpdate(bottom, CHUNK_SIZE - CHUNK_LAYER + blockIndex, intensityRed, intensityGreen, intensityBlue, light); // bottom->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (y < CHUNK_WIDTH - 1){ //top // removeLampNeighborUpdate(chunk, blockIndex + CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue, light); // } else if (top && top->isAccessible){ // vvox::swapLockedChunk(top, _lockedChunk); // removeLampNeighborUpdate(top, blockIndex - CHUNK_SIZE + CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue, light); // top->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // chunk->changeState(ChunkStates::MESH); } inline void placeLampNeighborUpdate(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 intensityRed VORB_UNUSED, ui16 intensityGreen VORB_UNUSED, ui16 intensityBlue VORB_UNUSED) { /* ui16 currentLight = chunk->getLampLight(blockIndex); const Block& block = chunk->getBlock(blockIndex); intensityRed = (ui16)((intensityRed >> LAMP_RED_SHIFT) * block.colorFilter.r) << LAMP_RED_SHIFT; intensityGreen = (ui16)((intensityGreen >> LAMP_GREEN_SHIFT) * block.colorFilter.g) << LAMP_GREEN_SHIFT; intensityBlue = (ui16)(intensityBlue * block.colorFilter.b); ui16 nextLight = getMaxLampColors(intensityRed, intensityGreen, intensityBlue, currentLight); if (nextLight != currentLight){ if (chunk->getBlock(blockIndex).allowLight){ chunk->setLampLight(blockIndex, nextLight); chunk->lampLightUpdateQueue.emplace(blockIndex, nextLight); } }*/ } void VoxelLightEngine::placeLampLightBFS(Chunk* chunk VORB_UNUSED, int blockIndex VORB_UNUSED, ui16 intensity VORB_UNUSED) { //#define RED1 0x400 //#define GREEN1 0x20 //#define BLUE1 1 // // ui16 currentLight = chunk->getLampLight(blockIndex); // // ui16 currentRed = currentLight & LAMP_RED_MASK; // ui16 currentGreen = currentLight & LAMP_GREEN_MASK; // ui16 currentBlue = currentLight & LAMP_BLUE_MASK; // // ui16 intensityRed = intensity & LAMP_RED_MASK; // ui16 intensityGreen = intensity & LAMP_GREEN_MASK; // ui16 intensityBlue = intensity & LAMP_BLUE_MASK; // // intensityRed = MAX(currentRed, intensityRed); // intensityGreen = MAX(currentGreen, intensityGreen); // intensityBlue = MAX(currentBlue, intensityBlue); // // const Block& currentBlock = chunk->getBlock(blockIndex); // // intensityRed = (ui16)((intensityRed >> LAMP_RED_SHIFT) * currentBlock.colorFilter.r) << LAMP_RED_SHIFT; // intensityGreen = (ui16)((intensityGreen >> LAMP_GREEN_SHIFT) * currentBlock.colorFilter.g) << LAMP_GREEN_SHIFT; // intensityBlue = (ui16)(intensityBlue * currentBlock.colorFilter.b); // intensity = intensityRed | intensityGreen | intensityBlue; // // if (intensity != currentLight) { // //Set the light value // chunk->setLampLight(blockIndex, intensity); // } // // if (intensityRed <= RED1 && intensityGreen <= GREEN1 && intensityBlue <= BLUE1) return; // //Reduce by 1 // if (intensityRed != 0) { // intensityRed -= RED1; // intensity -= RED1; // } // if (intensityGreen != 0) { // intensityGreen -= GREEN1; // intensity -= GREEN1; // } // if (intensityBlue != 0) { // intensityBlue -= BLUE1; // intensity -= BLUE1; // } // // chunk->dirty = 1; // // int x = blockIndex % CHUNK_WIDTH; // int y = blockIndex / CHUNK_LAYER; // int z = (blockIndex % CHUNK_LAYER) / CHUNK_WIDTH; // // Chunk*& left = chunk->left; // Chunk*& right = chunk->right; // Chunk*& back = chunk->back; // Chunk*& front = chunk->front; // Chunk*& top = chunk->top; // Chunk*& bottom = chunk->bottom; // // if (x > 0){ //left // placeLampNeighborUpdate(chunk, blockIndex - 1, intensityRed, intensityGreen, intensityBlue); // } else if (left && left->isAccessible){ // vvox::swapLockedChunk(left, _lockedChunk); // placeLampNeighborUpdate(left, blockIndex + CHUNK_WIDTH - 1, intensityRed, intensityGreen, intensityBlue); // left->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (x < CHUNK_WIDTH - 1){ //right // placeLampNeighborUpdate(chunk, blockIndex + 1, intensityRed, intensityGreen, intensityBlue); // } else if (right && right->isAccessible){ // vvox::swapLockedChunk(right, _lockedChunk); // placeLampNeighborUpdate(right, blockIndex - CHUNK_WIDTH + 1, intensityRed, intensityGreen, intensityBlue); // right->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (z > 0){ //back // placeLampNeighborUpdate(chunk, blockIndex - CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue); // } else if (back && back->isAccessible){ // vvox::swapLockedChunk(back, _lockedChunk); // placeLampNeighborUpdate(back, blockIndex + CHUNK_LAYER - CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue); // back->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (z < CHUNK_WIDTH - 1){ //front // placeLampNeighborUpdate(chunk, blockIndex + CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue); // } else if (front && front->isAccessible){ // vvox::swapLockedChunk(front, _lockedChunk); // placeLampNeighborUpdate(front, blockIndex - CHUNK_LAYER + CHUNK_WIDTH, intensityRed, intensityGreen, intensityBlue); // front->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // if (y > 0){ //bottom // placeLampNeighborUpdate(chunk, blockIndex - CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue); // } else if (bottom && bottom->isAccessible){ // vvox::swapLockedChunk(bottom, _lockedChunk); // placeLampNeighborUpdate(bottom, CHUNK_SIZE - CHUNK_LAYER + blockIndex, intensityRed, intensityGreen, intensityBlue); // bottom->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // if (y < CHUNK_WIDTH - 1){ //top // placeLampNeighborUpdate(chunk, blockIndex + CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue); // } else if (top && top->isAccessible){ // vvox::swapLockedChunk(top, _lockedChunk); // placeLampNeighborUpdate(top, blockIndex - CHUNK_SIZE + CHUNK_LAYER, intensityRed, intensityGreen, intensityBlue); // top->changeState(ChunkStates::MESH); // vvox::swapLockedChunk(chunk, _lockedChunk); // } // // chunk->changeState(ChunkStates::MESH); } ================================================ FILE: SoA/VoxelLightEngine.h ================================================ #pragma once #include //Used to tell neighbors to update light //class LightMessage { // LightMessage() {}; // LightMessage(ui16 BlockIndex, ui8 LightType, i8 LightValue) : blockIndex(BlockIndex), lightType(LightType), lightValue(LightValue) {} // ui16 blockIndex; // ui8 lightType; // i8 lightValue; //}; class SunlightRemovalNode { public: SunlightRemovalNode(ui16 BlockIndex, ui8 OldLightVal) : blockIndex(BlockIndex), oldLightVal(OldLightVal){} ui16 blockIndex; ui8 oldLightVal; }; class SunlightUpdateNode { public: SunlightUpdateNode(ui16 BlockIndex, ui8 LightVal) : blockIndex(BlockIndex), lightVal(LightVal){} ui16 blockIndex; ui8 lightVal; }; class LampLightRemovalNode { public: LampLightRemovalNode(ui16 BlockIndex, ui16 OldLightColor) : blockIndex(BlockIndex), oldLightColor(OldLightColor){} ui16 blockIndex; ui16 oldLightColor; }; class LampLightUpdateNode { public: LampLightUpdateNode(ui16 BlockIndex, ui16 LightColor) : blockIndex(BlockIndex), lightColor(LightColor){} ui16 blockIndex; ui16 lightColor; }; class Chunk; class VoxelLightEngine { public: void calculateLight(Chunk* chunk); void calculateSunlightExtend(Chunk* chunk); void calculateSunlightRemoval(Chunk* chunk); void checkTopForSunlight(Chunk* chunk); private: void blockSunRay(Chunk* chunk, int xz, int y); void extendSunRay(Chunk* chunk, int xz, int y); void removeSunlightBFS(Chunk* chunk, int blockIndex, ui8 oldLightVal); void placeSunlightBFS(Chunk* chunk, int blockIndex, ui8 intensity); void removeLampLightBFS(Chunk* chunk, int blockIndex, ui16 light); void placeLampLightBFS(Chunk* chunk, int blockIndex, ui16 intensity); // Chunk* _lockedChunk = nullptr; }; ================================================ FILE: SoA/VoxelMatrix.cpp ================================================ #include "stdafx.h" #include "VoxelMatrix.h" #include const ColorRGBA8& VoxelMatrix::getColor(const int index) const { return data[index]; } const ColorRGBA8& VoxelMatrix::getColor(const i32v3& position) const { return data[position.x + position.y * size.x + position.z * size.x * size.y]; } const ColorRGBA8& VoxelMatrix::getColor(const i32 x, const i32 y, const i32 z) const { return data[x + y * size.x + z * size.x * size.y]; } const ColorRGBA8& VoxelMatrix::getColorAndCheckBounds(const i32v3& position) const { if (position.x < 0 || position.x >= (i32)size.x) return color::Transparent; if (position.y < 0 || position.y >= (i32)size.y) return color::Transparent; if (position.z < 0 || position.z >= (i32)size.z) return color::Transparent; return data[position.x + position.y * size.x + position.z * size.x * size.y]; } const ColorRGBA8& VoxelMatrix::getColorAndCheckBounds(const i32 x, const i32 y, const i32 z) const { if (x < 0 || x >= (i32)size.x) return color::Transparent; if (y < 0 || y >= (i32)size.y) return color::Transparent; if (z < 0 || z >= (i32)size.z) return color::Transparent; return data[x + y * size.x + z * size.x * size.y]; } bool VoxelMatrix::isInterior(const i32v3& position) const { if (getColorAndCheckBounds(position + i32v3(1, 0, 0)).a == 0) return false; if (getColorAndCheckBounds(position + i32v3(0, 1, 0)).a == 0) return false; if (getColorAndCheckBounds(position + i32v3(0, 0, 1)).a == 0) return false; if (getColorAndCheckBounds(position + i32v3(-1, 0, 0)).a == 0) return false; if (getColorAndCheckBounds(position + i32v3(0, -1, 0)).a == 0) return false; if (getColorAndCheckBounds(position + i32v3(0, 0, -1)).a == 0) return false; return true; } bool VoxelMatrix::isInterior(const i32 x, const i32 y, const i32 z) const { return isInterior(i32v3(x, y, z)); } ================================================ FILE: SoA/VoxelMatrix.h ================================================ #pragma once #ifndef VoxelMatrix_h__ #define VoxelMatrix_h__ #include "Vorb/types.h" class VoxelMatrix { public: const ColorRGBA8& getColor(const int index) const; const ColorRGBA8& getColor(const i32v3& position) const; const ColorRGBA8& getColor(const i32 x, const i32 y, const i32 z) const; const ColorRGBA8& getColorAndCheckBounds(const i32v3& position) const; const ColorRGBA8& getColorAndCheckBounds(const i32 x, const i32 y, const i32 z) const; inline ui32 getIndex(const i32v3& position) const { return position.x + position.y * size.x + position.z * size.x * size.y; } inline ui32 getIndex(const i32 x, const i32 y, const i32 z) const { return x + y * size.x + z * size.x * size.y; } bool isInterior(const i32v3& position) const; bool isInterior(const i32 x, const i32 y, const i32 z) const; void dispose() { delete[] data; data = nullptr; } nString name = ""; ui32v3 size; i32v3 position; ColorRGBA8* data = nullptr; }; #endif //VoxelMatrix_h__ ================================================ FILE: SoA/VoxelMesh.h ================================================ /// /// VoxelMesh.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 24 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Graphics data for a voxel mesh /// #pragma once #ifndef VoxelMesh_h__ #define VoxelMesh_h__ #include /// Mesh data in a specific direction struct VoxelFaceDirectionMesh { public: VGVertexBuffer verts = 0; ///< Vertex data VGVertexArray vertexDeclarationSolid = 0; ///< Binding information for solid blocks VGVertexArray vertexDeclarationTransparent = 0; ///< Binding information for transparent blocks ui32 numSolid = 0; ///< Number of solid faces ui32 numTransparent = 0; ///< Number of transparent faces }; /// Mesh data for solid and transparent blocks struct VoxelMesh { public: VoxelFaceDirectionMesh meshes[6]; ///< Voxel mesh data for all 6 faces VGIndexBuffer indices = 0; ///< Reference to quad index buffer }; #endif // VoxelMesh_h__ ================================================ FILE: SoA/VoxelMesher.cpp ================================================ #include "stdafx.h" #include "VoxelMesher.h" // cube // // v6----- v5 // /| /| // v1------v0| // | | | | // | |v7---|-|v4 // |/ |/ // v2------v3 const GLfloat VoxelMesher::leafVertices[72] = { -0.0f, 1.0f, 0.5f, -0.0f, -0.0f, 0.5f, 1.0f, -0.0f, 0.5f, 1.0f, 1.0f, 0.5f, // v1-v2-v3-v0 (front) //WRONG!!! 0.5f, 1.0f, 1.0f, 0.5f, -0.0f, 1.0f, 0.5f, -0.0f, -0.0f, 0.5f, 1.0f, -0.0f, // v0-v3-v4-v5 (right) //WRONG!!! -0.0f, 0.5f, -0.0f, -0.0f, 0.5f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.5f, -0.0f, // v6-v1-v0-v5 (top) //WRONG!!! 0.5f, 1.0f, -0.0f, 0.5f, -0.0f, -0.0f, 0.5f, -0.0f, 1.0f, 0.5f, 1.0f, 1.0f, // v6-v7-v2-v1 (left) //WRONG!!! -0.0f, 0.5f, -0.0f, 1.0f, 0.5f, -0.0f, 1.0f, 0.5f, 1.0f, -0.0f, 0.5f, 1.0f, // v7-v4-v3-v2 (bottom) //WRONG!!! 1.0f, 1.0f, 0.5f, 1.0f, -0.0f, 0.5f, -0.0f, -0.0f, 0.5f, -0.0f, 1.0f, 0.5f }; // v5-v4-v7-v6 (back) //WRONG!!! // Cube Vertex Positional Resolution #define C_RES 7 const ui8v3 VoxelMesher::VOXEL_POSITIONS[NUM_FACES][4] = { { ui8v3(0, C_RES, 0), ui8v3(0, 0, 0), ui8v3(0, 0, C_RES), ui8v3(0, C_RES, C_RES) }, // v6-v7-v2-v1 (left) { ui8v3(C_RES, C_RES, C_RES), ui8v3(C_RES, 0, C_RES), ui8v3(C_RES, 0, 0), ui8v3(C_RES, C_RES, 0) }, // v0-v3-v4-v5 (right) { ui8v3(0, 0, C_RES), ui8v3(0, 0, 0), ui8v3(C_RES, 0, 0), ui8v3(C_RES, 0, C_RES) }, // v2-v7-v4-v3 (bottom) { ui8v3(C_RES, C_RES, C_RES), ui8v3(C_RES, C_RES, 0), ui8v3(0, C_RES, 0), ui8v3(0, C_RES, C_RES) }, // v0-v5-v6-v1 (top) { ui8v3(C_RES, C_RES, 0), ui8v3(C_RES, 0, 0), ui8v3(0, 0, 0), ui8v3(0, C_RES, 0) }, // v5-v4-v7-v6 (back) { ui8v3(0, C_RES, C_RES), ui8v3(0, 0, C_RES), ui8v3(C_RES, 0, C_RES), ui8v3(C_RES, C_RES, C_RES) } // v1-v2-v3-v0 (front) }; //0 = x, 1 = y, 2 = z const int VoxelMesher::cubeFaceAxis[6][2] = { { 0, 1 }, { 2, 1 }, { 0, 2 }, { 2, 1 }, { 0, 2 }, { 0, 1 } }; // front, right, top, left, bottom, back, for U and V respectively const int VoxelMesher::cubeFaceAxisSign[6][2] = { { 1, 1 }, { -1, 1 }, { 1, -1 }, { 1, 1 }, { -1, -1 }, { -1, 1 } }; // front, right, top, left, bottom, back, for U and V respectively const GLfloat VoxelMesher::liquidVertices[72] = { 0, 1.0f, 1.0f, 0, 0, 1.0f, 1.0f, 0, 1.0f, 1.0f, 1.0f, 1.0f, // v1-v2-v3-v0 (front) 1.0f, 1.0f, 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0, 1.0f, 1.0f, 0, // v0-v3-v4-v5 (right) 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0, // v6-v1-v0-v5 (top) 0, 1.0f, 0, 0, 0, 0, 0, 0, 1.0f, 0, 1.0f, 1.0f, // v6-v1.0f-v2-v1 (left) 1.0f, 0, 0, 1.0f, 0, 1.0f, 0, 0, 1.0f, 0, 0, 0, // v4-v3-v2-v1.0f (bottom) 1.0f, 1.0f, 0, 1.0f, 0, 0, 0, 0, 0, 0, 1.0f, 0 }; // v5-v4-v1.0f-v6 (back) const float wyOff = 0.9999f; const GLfloat VoxelMesher::waterCubeVertices[72] = { 0.0f, wyOff, 1.000f, 0.0f, 0.0f, 1.000f, 1.000f, 0.0f, wyOff, 1.000f, wyOff, 1.000f, // v1-v2-v3-v0 (front) 1.000f, wyOff, 1.000f, 1.000f, 0.0f, 1.000f, 1.000f, 0.0f, 0.0f, 1.000f, wyOff, 0.0f, // v0-v3-v4-v5 (right) 0.0f, wyOff, 0.0f, 0.0f, wyOff, 1.000f, 1.000f, wyOff, 1.000f, 1.000f, wyOff, 0.0f, // v6-v1-v0-v5 (top) 0.0f, wyOff, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.000f, 0.0f, wyOff, 1.000f, // v6-v7-v2-v1 (left) 0.0f, 0.0f, 0.0f, 1.000f, 0.0f, 0.0f, 1.000f, 0.0f, 1.000f, 0.0f, 0.0f, 1.000f, // v7-v4-v3-v2 (bottom) 1.000f, wyOff, 0.0f, 1.000f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, wyOff, 0.0f }; // v5-v4-v7-v6 (back) // 1 for normalized bytes #define N_1 127 const GLbyte VoxelMesher::cubeNormals[72] = { 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, // v1-v2-v3-v0 (front) N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, // v0-v3-v4-v5 (right) 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, // v6-v1-v0-v5 (top) -N_1, 0, 0, -N_1, 0, 0, -N_1, 0, 0, -N_1, 0, 0, // v6-v7-v2-v1 (left) 0, -N_1, 0, 0, -N_1, 0, 0, -N_1, 0, 0, -N_1, 0, // v4-v3-v2-v7 (bottom) 0, 0, -N_1, 0, 0, -N_1, 0, 0, -N_1, 0, 0, -N_1 }; // v5-v4-v7-v6 (back) //For flora, normal is strait up const GLbyte VoxelMesher::floraNormals[72] = { 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0, 0, N_1, 0 }; //We use 4 meshes so that we can add variation to the flora meshes const ui8v3 VoxelMesher::floraVertices[NUM_FLORA_MESHES][12] = { { ui8v3(0, 7, 5), ui8v3(0, 0, 5), ui8v3(7, 0, 5), ui8v3(7, 7, 5), ui8v3(6, 7, 7), ui8v3(6, 0, 7), ui8v3(1, 0, 0), ui8v3(1, 7, 0), ui8v3(1, 7, 7), ui8v3(1, 0, 7), ui8v3(6, 0, 0), ui8v3(6, 7, 0) }, { ui8v3(2, 7, 0), ui8v3(2, 0, 0), ui8v3(2, 0, 7), ui8v3(2, 7, 7), ui8v3(0, 7, 1), ui8v3(0, 0, 1), ui8v3(7, 0, 6), ui8v3(7, 7, 6), ui8v3(0, 7, 6), ui8v3(0, 0, 6), ui8v3(7, 0, 1), ui8v3(7, 7, 1) }, { ui8v3(0, 7, 2), ui8v3(0, 0, 2), ui8v3(7, 0, 2), ui8v3(7, 7, 2), ui8v3(6, 7, 0), ui8v3(6, 0, 0), ui8v3(1, 0, 7), ui8v3(1, 7, 7), ui8v3(1, 7, 0), ui8v3(1, 0, 0), ui8v3(6, 0, 7), ui8v3(6, 7, 7) }, { ui8v3(5, 7, 0), ui8v3(5, 0, 0), ui8v3(5, 0, 7), ui8v3(5, 7, 7), ui8v3(7, 7, 1), ui8v3(7, 0, 1), ui8v3(0, 0, 6), ui8v3(0, 7, 6), ui8v3(7, 7, 6), ui8v3(7, 0, 6), ui8v3(0, 0, 1), ui8v3(0, 7, 1) } }; const ui8v3 VoxelMesher::crossFloraVertices[NUM_CROSSFLORA_MESHES][8] = { { ui8v3(0, 7, 0), ui8v3(0, 0, 0), ui8v3(7, 0, 7), ui8v3(7, 7, 7), ui8v3(0, 7, 7), ui8v3(0, 0, 7), ui8v3(7, 0, 0), ui8v3(7, 7, 0) }, { ui8v3(7, 7, 7), ui8v3(7, 0, 7), ui8v3(0, 0, 0), ui8v3(0, 7, 0), ui8v3(7, 7, 0), ui8v3(7, 0, 0), ui8v3(0, 0, 7), ui8v3(0, 7, 7) } }; void VoxelMesher::makeFloraFace(BlockVertex *Verts VORB_UNUSED, const ui8* positions VORB_UNUSED, const i8* normals VORB_UNUSED, int vertexOffset VORB_UNUSED, int waveEffect VORB_UNUSED, i32v3& pos VORB_UNUSED, int vertexIndex VORB_UNUSED, int textureIndex VORB_UNUSED, int overlayTextureIndex VORB_UNUSED, const ColorRGB8& color VORB_UNUSED, const ColorRGB8& overlayColor VORB_UNUSED, const ui8 sunlight VORB_UNUSED, const ColorRGB8& lampColor VORB_UNUSED, const BlockTexture* texInfo VORB_UNUSED) { // TODO: Do we still want this? If so, reimplement and remove VORB_UNUSED tags. // // 7 per coord // pos.x *= POSITION_RESOLUTION; // pos.y *= POSITION_RESOLUTION; // pos.z *= POSITION_RESOLUTION; // // //Blend type. The 6 LSBs are used to encode alpha blending, add/subtract, and multiplication factors. // //They are used in the shader to determine how to blend. // ui8 blendMode = getBlendMode(texInfo->blendMode); // // Verts[vertexIndex].blendMode = blendMode; // Verts[vertexIndex + 1].blendMode = blendMode; // Verts[vertexIndex + 2].blendMode = blendMode; // Verts[vertexIndex + 3].blendMode = blendMode; // // GLubyte texAtlas = (GLubyte)(textureIndex / ATLAS_SIZE); // textureIndex %= ATLAS_SIZE; // // GLubyte overlayTexAtlas = (GLubyte)(overlayTextureIndex / ATLAS_SIZE); // GLubyte overlayTex = (GLubyte)(overlayTextureIndex % ATLAS_SIZE); // // Verts[vertexIndex].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 1].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 1].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 2].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 2].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 3].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 3].textureHeight = (ubyte)texInfo->base.size.y; // // Verts[vertexIndex].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 1].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 1].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 2].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 2].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 3].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 3].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // // Verts[vertexIndex].position.x = pos.x + positions[vertexOffset]; // Verts[vertexIndex].position.y = pos.y + positions[vertexOffset + 1]; // Verts[vertexIndex].position.z = pos.z + positions[vertexOffset + 2]; // Verts[vertexIndex + 1].position.x = pos.x + positions[vertexOffset + 3]; // Verts[vertexIndex + 1].position.y = pos.y + positions[vertexOffset + 4]; // Verts[vertexIndex + 1].position.z = pos.z + positions[vertexOffset + 5]; // Verts[vertexIndex + 2].position.x = pos.x + positions[vertexOffset + 6]; // Verts[vertexIndex + 2].position.y = pos.y + positions[vertexOffset + 7]; // Verts[vertexIndex + 2].position.z = pos.z + positions[vertexOffset + 8]; // Verts[vertexIndex + 3].position.x = pos.x + positions[vertexOffset + 9]; // Verts[vertexIndex + 3].position.y = pos.y + positions[vertexOffset + 10]; // Verts[vertexIndex + 3].position.z = pos.z + positions[vertexOffset + 11]; // // Verts[vertexIndex].color = color; // Verts[vertexIndex + 1].color = color; // Verts[vertexIndex + 2].color = color; // Verts[vertexIndex + 3].color = color; // // Verts[vertexIndex].overlayColor = overlayColor; // Verts[vertexIndex + 1].overlayColor = overlayColor; // Verts[vertexIndex + 2].overlayColor = overlayColor; // Verts[vertexIndex + 3].overlayColor = overlayColor; // // Verts[vertexIndex].normal[0] = normals[vertexOffset]; // Verts[vertexIndex].normal[1] = normals[vertexOffset + 1]; // Verts[vertexIndex].normal[2] = normals[vertexOffset + 2]; // Verts[vertexIndex + 1].normal[0] = normals[vertexOffset + 3]; // Verts[vertexIndex + 1].normal[1] = normals[vertexOffset + 4]; // Verts[vertexIndex + 1].normal[2] = normals[vertexOffset + 5]; // Verts[vertexIndex + 2].normal[0] = normals[vertexOffset + 6]; // Verts[vertexIndex + 2].normal[1] = normals[vertexOffset + 7]; // Verts[vertexIndex + 2].normal[2] = normals[vertexOffset + 8]; // Verts[vertexIndex + 3].normal[0] = normals[vertexOffset + 9]; // Verts[vertexIndex + 3].normal[1] = normals[vertexOffset + 10]; // Verts[vertexIndex + 3].normal[2] = normals[vertexOffset + 11]; // // Verts[vertexIndex].lampColor = lampColor; // Verts[vertexIndex + 1].lampColor = lampColor; // Verts[vertexIndex + 2].lampColor = lampColor; // Verts[vertexIndex + 3].lampColor = lampColor; // // // Verts[vertexIndex].sunlight = sunlight; // Verts[vertexIndex + 1].sunlight = sunlight; // Verts[vertexIndex + 2].sunlight = sunlight; // Verts[vertexIndex + 3].sunlight = sunlight; // // Verts[vertexIndex].merge = 0; // Verts[vertexIndex + 1].merge = 0; // Verts[vertexIndex + 2].merge = 0; // Verts[vertexIndex + 3].merge = 0; // // if (waveEffect == 2){ // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 255; // Verts[vertexIndex + 2].waveEffect = 255; // Verts[vertexIndex + 3].waveEffect = 255; // } else if (waveEffect == 1){ // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 255; // } else{ // Verts[vertexIndex].waveEffect = 0; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 0; // } // //#define UV_0 128 //#define UV_1 129 // // Verts[vertexIndex].tex[0] = UV_0; // Verts[vertexIndex].tex[1] = UV_1; // Verts[vertexIndex + 1].tex[0] = UV_0; // Verts[vertexIndex + 1].tex[1] = UV_0; // Verts[vertexIndex + 2].tex[0] = UV_1; // Verts[vertexIndex + 2].tex[1] = UV_0; // Verts[vertexIndex + 3].tex[0] = UV_1; // Verts[vertexIndex + 3].tex[1] = UV_1; // // // *********** Base Texture // Verts[vertexIndex].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 1].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 2].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 3].textureIndex = (GLubyte)textureIndex; // // Verts[vertexIndex].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 1].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 2].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 3].textureAtlas = (GLubyte)texAtlas; // // // *********** Overlay texture // Verts[vertexIndex].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 1].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 2].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 3].overlayTextureIndex = (GLubyte)overlayTex; // // Verts[vertexIndex].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 1].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 2].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 3].overlayTextureAtlas = (GLubyte)overlayTexAtlas; } void VoxelMesher::makeTransparentFace(BlockVertex *Verts VORB_UNUSED, const ui8* positions VORB_UNUSED, const i8* normals VORB_UNUSED, int vertexOffset VORB_UNUSED, int waveEffect VORB_UNUSED, i32v3& pos VORB_UNUSED, int vertexIndex VORB_UNUSED, int textureIndex VORB_UNUSED, int overlayTextureIndex VORB_UNUSED, const ColorRGB8& color VORB_UNUSED, const ColorRGB8& overlayColor VORB_UNUSED, const ui8 sunlight VORB_UNUSED, const ColorRGB8& lampColor VORB_UNUSED, const BlockTexture* texInfo VORB_UNUSED) { // TODO: Do we still want this? If so, reimplement and remove VORB_UNUSED tags. // // //get the face index so we can determine the axis alignment // int faceIndex = vertexOffset / 12; // //Multiply the axis by the sign bit to get the correct offset // GLubyte uOffset = (GLubyte)(pos[cubeFaceAxis[faceIndex][0]] * cubeFaceAxisSign[faceIndex][0]); // GLubyte vOffset = (GLubyte)(pos[cubeFaceAxis[faceIndex][1]] * cubeFaceAxisSign[faceIndex][1]); // // // 7 per coord // pos.x *= POSITION_RESOLUTION; // pos.y *= POSITION_RESOLUTION; // pos.z *= POSITION_RESOLUTION; // // //Blend type. The 6 LSBs are used to encode alpha blending, add/subtract, and multiplication factors. // //They are used in the shader to determine how to blend. // ui8 blendMode = getBlendMode(texInfo->blendMode); // // Verts[vertexIndex].blendMode = blendMode; // Verts[vertexIndex + 1].blendMode = blendMode; // Verts[vertexIndex + 2].blendMode = blendMode; // Verts[vertexIndex + 3].blendMode = blendMode; // // GLubyte texAtlas = (GLubyte)(textureIndex / ATLAS_SIZE); // textureIndex %= ATLAS_SIZE; // // GLubyte overlayTexAtlas = (GLubyte)(overlayTextureIndex / ATLAS_SIZE); // GLubyte overlayTex = (GLubyte)(overlayTextureIndex % ATLAS_SIZE); // // Verts[vertexIndex].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 1].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 1].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 2].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 2].textureHeight = (ubyte)texInfo->base.size.y; // Verts[vertexIndex + 3].textureWidth = (ubyte)texInfo->base.size.x; // Verts[vertexIndex + 3].textureHeight = (ubyte)texInfo->base.size.y; // // Verts[vertexIndex].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 1].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 1].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 2].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 2].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // Verts[vertexIndex + 3].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; // Verts[vertexIndex + 3].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; // // Verts[vertexIndex].position.x = pos.x + positions[vertexOffset]; // Verts[vertexIndex].position.y = pos.y + positions[vertexOffset + 1]; // Verts[vertexIndex].position.z = pos.z + positions[vertexOffset + 2]; // Verts[vertexIndex + 1].position.x = pos.x + positions[vertexOffset + 3]; // Verts[vertexIndex + 1].position.y = pos.y + positions[vertexOffset + 4]; // Verts[vertexIndex + 1].position.z = pos.z + positions[vertexOffset + 5]; // Verts[vertexIndex + 2].position.x = pos.x + positions[vertexOffset + 6]; // Verts[vertexIndex + 2].position.y = pos.y + positions[vertexOffset + 7]; // Verts[vertexIndex + 2].position.z = pos.z + positions[vertexOffset + 8]; // Verts[vertexIndex + 3].position.x = pos.x + positions[vertexOffset + 9]; // Verts[vertexIndex + 3].position.y = pos.y + positions[vertexOffset + 10]; // Verts[vertexIndex + 3].position.z = pos.z + positions[vertexOffset + 11]; // // Verts[vertexIndex].color = color; // Verts[vertexIndex + 1].color = color; // Verts[vertexIndex + 2].color = color; // Verts[vertexIndex + 3].color = color; // // Verts[vertexIndex].overlayColor = overlayColor; // Verts[vertexIndex + 1].overlayColor = overlayColor; // Verts[vertexIndex + 2].overlayColor = overlayColor; // Verts[vertexIndex + 3].overlayColor = overlayColor; // // Verts[vertexIndex].normal[0] = normals[vertexOffset]; // Verts[vertexIndex].normal[1] = normals[vertexOffset + 1]; // Verts[vertexIndex].normal[2] = normals[vertexOffset + 2]; // Verts[vertexIndex + 1].normal[0] = normals[vertexOffset + 3]; // Verts[vertexIndex + 1].normal[1] = normals[vertexOffset + 4]; // Verts[vertexIndex + 1].normal[2] = normals[vertexOffset + 5]; // Verts[vertexIndex + 2].normal[0] = normals[vertexOffset + 6]; // Verts[vertexIndex + 2].normal[1] = normals[vertexOffset + 7]; // Verts[vertexIndex + 2].normal[2] = normals[vertexOffset + 8]; // Verts[vertexIndex + 3].normal[0] = normals[vertexOffset + 9]; // Verts[vertexIndex + 3].normal[1] = normals[vertexOffset + 10]; // Verts[vertexIndex + 3].normal[2] = normals[vertexOffset + 11]; // // Verts[vertexIndex].lampColor = lampColor; // Verts[vertexIndex + 1].lampColor = lampColor; // Verts[vertexIndex + 2].lampColor = lampColor; // Verts[vertexIndex + 3].lampColor = lampColor; // // // Verts[vertexIndex].sunlight = sunlight; // Verts[vertexIndex + 1].sunlight = sunlight; // Verts[vertexIndex + 2].sunlight = sunlight; // Verts[vertexIndex + 3].sunlight = sunlight; // // Verts[vertexIndex].merge = 0; // Verts[vertexIndex + 1].merge = 0; // Verts[vertexIndex + 2].merge = 0; // Verts[vertexIndex + 3].merge = 0; // // if (waveEffect == 2) { // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 255; // Verts[vertexIndex + 2].waveEffect = 255; // Verts[vertexIndex + 3].waveEffect = 255; // } else if (waveEffect == 1) { // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 255; // } else { // Verts[vertexIndex].waveEffect = 0; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 0; // } // //#define UV_0 128 //#define UV_1 129 // // Verts[vertexIndex].tex[0] = UV_0 + uOffset; // Verts[vertexIndex].tex[1] = UV_1 + vOffset; // Verts[vertexIndex + 1].tex[0] = UV_0 + uOffset; // Verts[vertexIndex + 1].tex[1] = UV_0 + vOffset; // Verts[vertexIndex + 2].tex[0] = UV_1 + uOffset; // Verts[vertexIndex + 2].tex[1] = UV_0 + vOffset; // Verts[vertexIndex + 3].tex[0] = UV_1 + uOffset; // Verts[vertexIndex + 3].tex[1] = UV_1 + vOffset; // // // *********** Base Texture // Verts[vertexIndex].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 1].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 2].textureIndex = (GLubyte)textureIndex; // Verts[vertexIndex + 3].textureIndex = (GLubyte)textureIndex; // // Verts[vertexIndex].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 1].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 2].textureAtlas = (GLubyte)texAtlas; // Verts[vertexIndex + 3].textureAtlas = (GLubyte)texAtlas; // // // *********** Overlay texture // Verts[vertexIndex].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 1].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 2].overlayTextureIndex = (GLubyte)overlayTex; // Verts[vertexIndex + 3].overlayTextureIndex = (GLubyte)overlayTex; // // Verts[vertexIndex].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 1].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 2].overlayTextureAtlas = (GLubyte)overlayTexAtlas; // Verts[vertexIndex + 3].overlayTextureAtlas = (GLubyte)overlayTexAtlas; } void VoxelMesher::makeCubeFace(BlockVertex *Verts VORB_UNUSED, int vertexOffset VORB_UNUSED, int waveEffect VORB_UNUSED, i32v3& pos VORB_UNUSED, int vertexIndex VORB_UNUSED, int textureIndex VORB_UNUSED, int overlayTextureIndex VORB_UNUSED, const ColorRGB8& color VORB_UNUSED, const ColorRGB8& overlayColor VORB_UNUSED, GLfloat ambientOcclusion VORB_UNUSED[], const BlockTexture* texInfo VORB_UNUSED) { // TODO: Do we still want this? If so, reimplement and remove VORB_UNUSED tags. ////get the face index so we can determine the axis alignment //int faceIndex = vertexOffset / 12; ////Multiply the axis by the sign bit to get the correct offset //GLubyte uOffset = (GLubyte)(pos[cubeFaceAxis[faceIndex][0]] * cubeFaceAxisSign[faceIndex][0]); //GLubyte vOffset = (GLubyte)(pos[cubeFaceAxis[faceIndex][1]] * cubeFaceAxisSign[faceIndex][1]); //const GLubyte* cverts = cubeVertices; //// 7 per coord //pos.x *= POSITION_RESOLUTION; //pos.y *= POSITION_RESOLUTION; //pos.z *= POSITION_RESOLUTION; ////Blend type. The 6 LSBs are used to encode alpha blending, add/subtract, and multiplication factors. ////They are used in the shader to determine how to blend. //ui8 blendMode = getBlendMode(texInfo->blendMode); // //Verts[vertexIndex].blendMode = blendMode; //Verts[vertexIndex + 1].blendMode = blendMode; //Verts[vertexIndex + 2].blendMode = blendMode; //Verts[vertexIndex + 3].blendMode = blendMode; //GLubyte texAtlas = (GLubyte)(textureIndex / ATLAS_SIZE); //textureIndex %= ATLAS_SIZE; //GLubyte overlayTexAtlas = (GLubyte)(overlayTextureIndex / ATLAS_SIZE); //GLubyte overlayTex = (GLubyte)(overlayTextureIndex % ATLAS_SIZE); //Verts[vertexIndex].textureWidth = (ubyte)texInfo->base.size.x; //Verts[vertexIndex].textureHeight = (ubyte)texInfo->base.size.y; //Verts[vertexIndex + 1].textureWidth = (ubyte)texInfo->base.size.x; //Verts[vertexIndex + 1].textureHeight = (ubyte)texInfo->base.size.y; //Verts[vertexIndex + 2].textureWidth = (ubyte)texInfo->base.size.x; //Verts[vertexIndex + 2].textureHeight = (ubyte)texInfo->base.size.y; //Verts[vertexIndex + 3].textureWidth = (ubyte)texInfo->base.size.x; //Verts[vertexIndex + 3].textureHeight = (ubyte)texInfo->base.size.y; //Verts[vertexIndex].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; //Verts[vertexIndex].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; //Verts[vertexIndex + 1].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; //Verts[vertexIndex + 1].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; //Verts[vertexIndex + 2].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; //Verts[vertexIndex + 2].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; //Verts[vertexIndex + 3].overlayTextureWidth = (ubyte)texInfo->overlay.size.x; //Verts[vertexIndex + 3].overlayTextureHeight = (ubyte)texInfo->overlay.size.y; //Verts[vertexIndex].position.x = pos.x + cverts[vertexOffset]; //Verts[vertexIndex].position.y = pos.y + cverts[vertexOffset + 1]; //Verts[vertexIndex].position.z = pos.z + cverts[vertexOffset + 2]; //Verts[vertexIndex + 1].position.x = pos.x + cverts[vertexOffset + 3]; //Verts[vertexIndex + 1].position.y = pos.y + cverts[vertexOffset + 4]; //Verts[vertexIndex + 1].position.z = pos.z + cverts[vertexOffset + 5]; //Verts[vertexIndex + 2].position.x = pos.x + cverts[vertexOffset + 6]; //Verts[vertexIndex + 2].position.y = pos.y + cverts[vertexOffset + 7]; //Verts[vertexIndex + 2].position.z = pos.z + cverts[vertexOffset + 8]; //Verts[vertexIndex + 3].position.x = pos.x + cverts[vertexOffset + 9]; //Verts[vertexIndex + 3].position.y = pos.y + cverts[vertexOffset + 10]; //Verts[vertexIndex + 3].position.z = pos.z + cverts[vertexOffset + 11]; //Verts[vertexIndex].color.r = (GLubyte)(color.r * ambientOcclusion[0]); //Verts[vertexIndex].color.g = (GLubyte)(color.g * ambientOcclusion[0]); //Verts[vertexIndex].color.b = (GLubyte)(color.b * ambientOcclusion[0]); //Verts[vertexIndex + 1].color.r = (GLubyte)(color.r * ambientOcclusion[1]); //Verts[vertexIndex + 1].color.g = (GLubyte)(color.g * ambientOcclusion[1]); //Verts[vertexIndex + 1].color.b = (GLubyte)(color.b * ambientOcclusion[1]); //Verts[vertexIndex + 2].color.r = (GLubyte)(color.r * ambientOcclusion[2]); //Verts[vertexIndex + 2].color.g = (GLubyte)(color.g * ambientOcclusion[2]); //Verts[vertexIndex + 2].color.b = (GLubyte)(color.b * ambientOcclusion[2]); //Verts[vertexIndex + 3].color.r = (GLubyte)(color.r * ambientOcclusion[3]); //Verts[vertexIndex + 3].color.g = (GLubyte)(color.g * ambientOcclusion[3]); //Verts[vertexIndex + 3].color.b = (GLubyte)(color.b * ambientOcclusion[3]); //Verts[vertexIndex].overlayColor.r = (GLubyte)(overlayColor.r * ambientOcclusion[0]); //Verts[vertexIndex].overlayColor.g = (GLubyte)(overlayColor.g * ambientOcclusion[0]); //Verts[vertexIndex].overlayColor.b = (GLubyte)(overlayColor.b * ambientOcclusion[0]); //Verts[vertexIndex + 1].overlayColor.r = (GLubyte)(overlayColor.r * ambientOcclusion[1]); //Verts[vertexIndex + 1].overlayColor.g = (GLubyte)(overlayColor.g * ambientOcclusion[1]); //Verts[vertexIndex + 1].overlayColor.b = (GLubyte)(overlayColor.b * ambientOcclusion[1]); //Verts[vertexIndex + 2].overlayColor.r = (GLubyte)(overlayColor.r * ambientOcclusion[2]); //Verts[vertexIndex + 2].overlayColor.g = (GLubyte)(overlayColor.g * ambientOcclusion[2]); //Verts[vertexIndex + 2].overlayColor.b = (GLubyte)(overlayColor.b * ambientOcclusion[2]); //Verts[vertexIndex + 3].overlayColor.r = (GLubyte)(overlayColor.r * ambientOcclusion[3]); //Verts[vertexIndex + 3].overlayColor.g = (GLubyte)(overlayColor.g * ambientOcclusion[3]); //Verts[vertexIndex + 3].overlayColor.b = (GLubyte)(overlayColor.b * ambientOcclusion[3]); //Verts[vertexIndex].normal[0] = cubeNormals[vertexOffset]; //Verts[vertexIndex].normal[1] = cubeNormals[vertexOffset + 1]; //Verts[vertexIndex].normal[2] = cubeNormals[vertexOffset + 2]; //Verts[vertexIndex + 1].normal[0] = cubeNormals[vertexOffset + 3]; //Verts[vertexIndex + 1].normal[1] = cubeNormals[vertexOffset + 4]; //Verts[vertexIndex + 1].normal[2] = cubeNormals[vertexOffset + 5]; //Verts[vertexIndex + 2].normal[0] = cubeNormals[vertexOffset + 6]; //Verts[vertexIndex + 2].normal[1] = cubeNormals[vertexOffset + 7]; //Verts[vertexIndex + 2].normal[2] = cubeNormals[vertexOffset + 8]; //Verts[vertexIndex + 3].normal[0] = cubeNormals[vertexOffset + 9]; //Verts[vertexIndex + 3].normal[1] = cubeNormals[vertexOffset + 10]; //Verts[vertexIndex + 3].normal[2] = cubeNormals[vertexOffset + 11]; //Verts[vertexIndex].merge = 1; //Verts[vertexIndex + 1].merge = 1; //Verts[vertexIndex + 2].merge = 1; //Verts[vertexIndex + 3].merge = 1; //if (waveEffect == 2){ // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 255; // Verts[vertexIndex + 2].waveEffect = 255; // Verts[vertexIndex + 3].waveEffect = 255; //} else if (waveEffect == 1){ // Verts[vertexIndex].waveEffect = 255; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 255; //} else{ // Verts[vertexIndex].waveEffect = 0; // Verts[vertexIndex + 1].waveEffect = 0; // Verts[vertexIndex + 2].waveEffect = 0; // Verts[vertexIndex + 3].waveEffect = 0; //} //#define UV_0 128 //#define UV_1 129 //Verts[vertexIndex].tex[0] = UV_0 + uOffset; //Verts[vertexIndex].tex[1] = UV_1 + vOffset; //Verts[vertexIndex + 1].tex[0] = UV_0 + uOffset; //Verts[vertexIndex + 1].tex[1] = UV_0 + vOffset; //Verts[vertexIndex + 2].tex[0] = UV_1 + uOffset; //Verts[vertexIndex + 2].tex[1] = UV_0 + vOffset; //Verts[vertexIndex + 3].tex[0] = UV_1 + uOffset; //Verts[vertexIndex + 3].tex[1] = UV_1 + vOffset; //// *********** Base Texture //Verts[vertexIndex].textureIndex = (GLubyte)textureIndex; //Verts[vertexIndex + 1].textureIndex = (GLubyte)textureIndex; //Verts[vertexIndex + 2].textureIndex = (GLubyte)textureIndex; //Verts[vertexIndex + 3].textureIndex = (GLubyte)textureIndex; //Verts[vertexIndex].textureAtlas = (GLubyte)texAtlas; //Verts[vertexIndex + 1].textureAtlas = (GLubyte)texAtlas; //Verts[vertexIndex + 2].textureAtlas = (GLubyte)texAtlas; //Verts[vertexIndex + 3].textureAtlas = (GLubyte)texAtlas; //// *********** Overlay texture //Verts[vertexIndex].overlayTextureIndex = (GLubyte)overlayTex; //Verts[vertexIndex + 1].overlayTextureIndex = (GLubyte)overlayTex; //Verts[vertexIndex + 2].overlayTextureIndex = (GLubyte)overlayTex; //Verts[vertexIndex + 3].overlayTextureIndex = (GLubyte)overlayTex; //Verts[vertexIndex].overlayTextureAtlas = (GLubyte)overlayTexAtlas; //Verts[vertexIndex + 1].overlayTextureAtlas = (GLubyte)overlayTexAtlas; //Verts[vertexIndex + 2].overlayTextureAtlas = (GLubyte)overlayTexAtlas; //Verts[vertexIndex + 3].overlayTextureAtlas = (GLubyte)overlayTexAtlas; } const GLubyte waterUVs[8] = { 0, 7, 0, 0, 7, 0, 7, 7 }; void VoxelMesher::makeLiquidFace(std::vector& verts, i32 index, ui8 uOff, ui8 vOff, const ColorRGB8& lampColor, ui8 sunlight, const ColorRGB8& color, ui8 textureUnit) { verts.resize(verts.size() + 4); verts[index].tex[0] = waterUVs[0] + uOff; verts[index].tex[1] = waterUVs[1] + vOff; verts[index + 1].tex[0] = waterUVs[2] + uOff; verts[index + 1].tex[1] = waterUVs[3] + vOff; verts[index + 2].tex[0] = waterUVs[4] + uOff; verts[index + 2].tex[1] = waterUVs[5] + vOff; verts[index + 3].tex[0] = waterUVs[6] + uOff; verts[index + 3].tex[1] = waterUVs[7] + vOff; verts[index].lampColor = lampColor; verts[index + 1].lampColor = lampColor; verts[index + 2].lampColor = lampColor; verts[index + 3].lampColor = lampColor; verts[index].sunlight = sunlight; verts[index + 1].sunlight = sunlight; verts[index + 2].sunlight = sunlight; verts[index + 3].sunlight = sunlight; verts[index].color.r = color.r; verts[index].color.g = color.g; verts[index].color.b = color.b; verts[index + 1].color.r = color.r; verts[index + 1].color.g = color.g; verts[index + 1].color.b = color.b; verts[index + 2].color.r = color.r; verts[index + 2].color.g = color.g; verts[index + 2].color.b = color.b; verts[index + 3].color.r = color.r; verts[index + 3].color.g = color.g; verts[index + 3].color.b = color.b; verts[index].textureUnit = (GLubyte)textureUnit; verts[index + 1].textureUnit = (GLubyte)textureUnit; verts[index + 2].textureUnit = (GLubyte)textureUnit; verts[index + 3].textureUnit = (GLubyte)textureUnit; } void VoxelMesher::makePhysicsBlockFace(std::vector &verts VORB_UNUSED, int vertexOffset VORB_UNUSED, int &index VORB_UNUSED, const BlockTexture& blockTexture VORB_UNUSED) { // TODO: Do we still want this? If so, reimplement and remove VORB_UNUSED tags. /* ui8 textureAtlas = (ui8)(blockTexture.base.index / ATLAS_SIZE); ui8 textureIndex = (ui8)(blockTexture.base.index % ATLAS_SIZE); ui8 overlayTextureAtlas = (ui8)(blockTexture.overlay.index / ATLAS_SIZE); ui8 overlayTextureIndex = (ui8)(blockTexture.overlay.index % ATLAS_SIZE); ui8 blendMode = getBlendMode(blockTexture.blendMode); const GLubyte* cverts = cubeVertices; verts[index].blendMode = blendMode; verts[index + 1].blendMode = blendMode; verts[index + 2].blendMode = blendMode; verts[index + 3].blendMode = blendMode; verts[index + 4].blendMode = blendMode; verts[index + 5].blendMode = blendMode; verts[index].textureWidth = (ui8)blockTexture.base.size.x; verts[index].textureHeight = (ui8)blockTexture.base.size.y; verts[index + 1].textureWidth = (ui8)blockTexture.base.size.x; verts[index + 1].textureHeight = (ui8)blockTexture.base.size.y; verts[index + 2].textureWidth = (ui8)blockTexture.base.size.x; verts[index + 2].textureHeight = (ui8)blockTexture.base.size.y; verts[index + 3].textureWidth = (ui8)blockTexture.base.size.x; verts[index + 3].textureHeight = (ui8)blockTexture.base.size.y; verts[index + 4].textureWidth = (ui8)blockTexture.base.size.x; verts[index + 4].textureHeight = (ui8)blockTexture.base.size.y; verts[index + 5].textureWidth = (ui8)blockTexture.base.size.x; verts[index + 5].textureHeight = (ui8)blockTexture.base.size.y; verts[index].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index + 1].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index + 1].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index + 2].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index + 2].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index + 3].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index + 3].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index + 4].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index + 4].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index + 5].overlayTextureWidth = (ui8)blockTexture.overlay.size.x; verts[index + 5].overlayTextureHeight = (ui8)blockTexture.overlay.size.y; verts[index].normal[0] = cubeNormals[vertexOffset]; verts[index].normal[1] = cubeNormals[vertexOffset + 1]; verts[index].normal[2] = cubeNormals[vertexOffset + 2]; verts[index + 1].normal[0] = cubeNormals[vertexOffset + 3]; verts[index + 1].normal[1] = cubeNormals[vertexOffset + 4]; verts[index + 1].normal[2] = cubeNormals[vertexOffset + 5]; verts[index + 2].normal[0] = cubeNormals[vertexOffset + 6]; verts[index + 2].normal[1] = cubeNormals[vertexOffset + 7]; verts[index + 2].normal[2] = cubeNormals[vertexOffset + 8]; verts[index + 3].normal[0] = cubeNormals[vertexOffset + 6]; verts[index + 3].normal[1] = cubeNormals[vertexOffset + 7]; verts[index + 3].normal[2] = cubeNormals[vertexOffset + 8]; verts[index + 4].normal[0] = cubeNormals[vertexOffset + 9]; verts[index + 4].normal[1] = cubeNormals[vertexOffset + 10]; verts[index + 4].normal[2] = cubeNormals[vertexOffset + 11]; verts[index + 5].normal[0] = cubeNormals[vertexOffset]; verts[index + 5].normal[1] = cubeNormals[vertexOffset + 1]; verts[index + 5].normal[2] = cubeNormals[vertexOffset + 2]; verts[index].position[0] = cverts[vertexOffset]; verts[index].position[1] = cverts[vertexOffset + 1]; verts[index].position[2] = cverts[vertexOffset + 2]; verts[index + 1].position[0] = cverts[vertexOffset + 3]; verts[index + 1].position[1] = cverts[vertexOffset + 4]; verts[index + 1].position[2] = cverts[vertexOffset + 5]; verts[index + 2].position[0] = cverts[vertexOffset + 6]; verts[index + 2].position[1] = cverts[vertexOffset + 7]; verts[index + 2].position[2] = cverts[vertexOffset + 8]; verts[index + 3].position[0] = cverts[vertexOffset + 6]; verts[index + 3].position[1] = cverts[vertexOffset + 7]; verts[index + 3].position[2] = cverts[vertexOffset + 8]; verts[index + 4].position[0] = cverts[vertexOffset + 9]; verts[index + 4].position[1] = cverts[vertexOffset + 10]; verts[index + 4].position[2] = cverts[vertexOffset + 11]; verts[index + 5].position[0] = cverts[vertexOffset]; verts[index + 5].position[1] = cverts[vertexOffset + 1]; verts[index + 5].position[2] = cverts[vertexOffset + 2]; #define UV_0 128 #define UV_1 129 verts[index].tex[0] = UV_0; verts[index].tex[1] = UV_1; verts[index + 1].tex[0] = UV_0; verts[index + 1].tex[1] = UV_0; verts[index + 2].tex[0] = UV_1; verts[index + 2].tex[1] = UV_0; verts[index + 3].tex[0] = UV_1; verts[index + 3].tex[1] = UV_0; verts[index + 4].tex[0] = UV_1; verts[index + 4].tex[1] = UV_1; verts[index + 5].tex[0] = UV_0; verts[index + 5].tex[1] = UV_1; verts[index].textureAtlas = textureAtlas; verts[index + 1].textureAtlas = textureAtlas; verts[index + 2].textureAtlas = textureAtlas; verts[index + 3].textureAtlas = textureAtlas; verts[index + 4].textureAtlas = textureAtlas; verts[index + 5].textureAtlas = textureAtlas; verts[index].textureIndex = textureIndex; verts[index + 1].textureIndex = textureIndex; verts[index + 2].textureIndex = textureIndex; verts[index + 3].textureIndex = textureIndex; verts[index + 4].textureIndex = textureIndex; verts[index + 5].textureIndex = textureIndex; verts[index].overlayTextureAtlas = overlayTextureAtlas; verts[index + 1].overlayTextureAtlas = overlayTextureAtlas; verts[index + 2].overlayTextureAtlas = overlayTextureAtlas; verts[index + 3].overlayTextureAtlas = overlayTextureAtlas; verts[index + 4].overlayTextureAtlas = overlayTextureAtlas; verts[index + 5].overlayTextureAtlas = overlayTextureAtlas; verts[index].overlayTextureIndex = overlayTextureIndex; verts[index + 1].overlayTextureIndex = overlayTextureIndex; verts[index + 2].overlayTextureIndex = overlayTextureIndex; verts[index + 3].overlayTextureIndex = overlayTextureIndex; verts[index + 4].overlayTextureIndex = overlayTextureIndex; verts[index + 5].overlayTextureIndex = overlayTextureIndex;*/ } ui8 VoxelMesher::getBlendMode(const BlendType& blendType) { ubyte blendMode = 0x14; //0x14 = 00 01 01 00 switch (blendType) { case BlendType::ALPHA: blendMode |= 1; //Sets blendMode to 00 01 01 01 break; case BlendType::ADD: blendMode += 4; //Sets blendMode to 00 01 10 00 break; case BlendType::SUBTRACT: blendMode -= 4; //Sets blendMode to 00 01 00 00 break; case BlendType::MULTIPLY: blendMode -= 16; //Sets blendMode to 00 00 01 00 break; } return blendMode; } ================================================ FILE: SoA/VoxelMesher.h ================================================ #pragma once #include "BlockData.h" #include "Vertex.h" #define NUM_FLORA_MESHES 4 #define NUM_FLORA_VERTICES 36 #define NUM_CROSSFLORA_MESHES 2 #define NUM_CROSSFLORA_VERTICES 24 // Offsets into CubeVertices #define CUBE_FACE_0_VERTEX_OFFSET 0 #define CUBE_FACE_1_VERTEX_OFFSET 12 #define CUBE_FACE_2_VERTEX_OFFSET 24 #define CUBE_FACE_3_VERTEX_OFFSET 36 #define CUBE_FACE_4_VERTEX_OFFSET 48 #define CUBE_FACE_5_VERTEX_OFFSET 60 //Provides helpful meshing functions for voxels class VoxelMesher { public: static void makeFloraFace(BlockVertex *Verts, const ui8* positions, const i8* normals, int vertexOffset, int waveEffect, i32v3& pos, int vertexIndex, int textureIndex, int overlayTextureIndex, const ColorRGB8& color, const ColorRGB8& overlayColor, const ui8 sunlight, const ColorRGB8& lampColor, const BlockTexture* texInfo); static void makeTransparentFace(BlockVertex *Verts, const ui8* positions, const i8* normals, int vertexOffset, int waveEffect, i32v3& pos, int vertexIndex, int textureIndex, int overlayTextureIndex, const ColorRGB8& color, const ColorRGB8& overlayColor, const ui8 sunlight, const ColorRGB8& lampColor, const BlockTexture* texInfo); static void makeCubeFace(BlockVertex *Verts, int vertexOffset, int waveEffect, i32v3& pos, int vertexIndex, int textureIndex, int overlayTextureIndex, const ColorRGB8& color, const ColorRGB8& overlayColor, GLfloat ambientOcclusion[], const BlockTexture* texInfo); static void makeLiquidFace(std::vector& verts, i32 index, ui8 uOff, ui8 vOff, const ColorRGB8& lampColor, ui8 sunlight, const ColorRGB8& color, ui8 textureUnit); static void makePhysicsBlockFace(std::vector &verts, int vertexOffset, int &index, const BlockTexture& blockTexture); static ui8 getBlendMode(const BlendType& blendType); #define POSITION_RESOLUTION 7 #define ATLAS_SIZE 256 #define ATLAS_MODULUS_BITS 0xFF #define NUM_FACES 6 #define NUM_VERTICES 72 static const ui8v3 VOXEL_POSITIONS[NUM_FACES][4]; static const GLfloat leafVertices[NUM_VERTICES]; static const int cubeFaceAxis[NUM_FACES][2]; static const int cubeFaceAxisSign[NUM_FACES][2]; static const GLfloat liquidVertices[NUM_VERTICES]; static const GLfloat waterCubeVertices[NUM_VERTICES]; static const GLbyte cubeNormals[NUM_VERTICES]; static const GLbyte floraNormals[NUM_VERTICES]; static const ui8v3 floraVertices[NUM_FLORA_MESHES][12]; static const ui8v3 crossFloraVertices[NUM_CROSSFLORA_MESHES][8]; }; ================================================ FILE: SoA/VoxelModel.cpp ================================================ #include "stdafx.h" #include "VoxelModel.h" #include "VoxelMatrix.h" #include "VoxelModelLoader.h" #include "ModelMesher.h" VoxelModel::VoxelModel(): m_mesh() { // Empty } VoxelModel::~VoxelModel() { m_matrix.dispose(); } bool VoxelModel::loadFromFile(const nString& path) { VoxelMatrix matrix; if (!VoxelModelLoader::loadModel(path, matrix)) { return false; } setMatrix(matrix); return true; } ================================================ FILE: SoA/VoxelModel.h ================================================ #pragma once #ifndef VoxelModel_h__ #define VoxelModel_h__ #include #include #include #include "VoxelModelMesh.h" #include "VoxelMatrix.h" class VoxelModelVertex; class VoxelModel { public: VoxelModel(); ~VoxelModel(); bool loadFromFile(const nString& path); void setMatrix(const VoxelMatrix& matrix) { m_matrix = matrix; } void setMesh(const VoxelModelMesh& mesh) { m_mesh = mesh; } VoxelMatrix& getMatrix() { return m_matrix; } const VoxelMatrix& getMatrix() const { return m_matrix; } const VoxelModelMesh& getMesh() const { return m_mesh; } private: VoxelMatrix m_matrix; VoxelModelMesh m_mesh; }; #endif //VoxelModel_h__ ================================================ FILE: SoA/VoxelModelLoader.cpp ================================================ #include "stdafx.h" #include "VoxelModelLoader.h" #include #include #include "VoxelMatrix.h" VoxelModelLoader::VoxelModelLoader() { //Empty } bool VoxelModelLoader::loadModel(const nString& filePath, VoxelMatrix& matrix /* Even though .qb can have multiple matrices, we assume a single matrix. */) { FILE* file = NULL; file=fopen( filePath.c_str(), "rb"); bool ok = true; ui8* version = new ui8[4]; ok = fread(&version[0], sizeof(char)*4, 1, file) == 1; ui32 colorFormat; ok = fread(&colorFormat, sizeof(ui32), 1, file) == 1; ui32 zAxisOrientation; ok = fread(&zAxisOrientation, sizeof(ui32), 1, file) == 1; ui32 compressed; ok = fread(&compressed, sizeof(ui32), 1, file) == 1; ui32 visibilityMaskEncoded; ok = fread(&visibilityMaskEncoded, sizeof(ui32), 1, file) == 1; ui32 numMatrices; ok = fread(&numMatrices, sizeof(ui32), 1, file) == 1; char nameLength = 0; ok = fread((char*)&nameLength, sizeof(char), 1, file) == 1; char* name = new char[nameLength + 1]; ok = fread(name, sizeof(char)*nameLength, 1, file) == 1; name[(size_t)nameLength] = 0; printf("%s", name); ok = fread(&matrix.size.x, sizeof(ui32), 1, file) == 1; ok = fread(&matrix.size.y, sizeof(ui32), 1, file) == 1; ok = fread(&matrix.size.z, sizeof(ui32), 1, file) == 1; ok = fread(&matrix.position.x, sizeof(ui32), 1, file) == 1; ok = fread(&matrix.position.y, sizeof(ui32), 1, file) == 1; ok = fread(&matrix.position.z, sizeof(ui32), 1, file) == 1; matrix.data = new ColorRGBA8[matrix.size.x * matrix.size.y * matrix.size.z]; if(compressed == 0) { // Uncompressed Data for(ui32 z = 0; z < matrix.size.z; z++) { for(ui32 y = 0; y < matrix.size.y; y++) { for(ui32 x = 0; x < matrix.size.x; x++) { ui32 data = 0; ok = fread(&data, sizeof(ui32), 1, file) == 1; ui32 r = data & 0x000000ff; ui32 g = (data & 0x0000ff00) >> 8; ui32 b = (data & 0x00ff0000) >> 16; ui32 a = (data & 0xff000000) >> 24; int location = matrix.getIndex(x, y, z); matrix.data[location] = ColorRGBA8(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); } } } } else { // RLE compressed ui32 z = 0; while(z < matrix.size.z) { ui32 index = 0; while(true) { ui32 data = 0; ok = fread(&data, sizeof(ui32), 1, file) == 1; if(data == NEXT_SLICE_FLAG) { break; } else if(data == CODE_FLAG) { ui32 count = 0; ok = fread(&count, sizeof(ui32), 1, file) == 1; ok = fread(&data, sizeof(ui32), 1, file) == 1; for(ui32 j = 0; j < count; j++) { i32 x = index % matrix.size.x; i32 y = index / matrix.size.x; index++; ui32 r = data & 0x000000ff; ui32 g = (data & 0x0000ff00) >> 8; ui32 b = (data & 0x00ff0000) >> 16; ui32 a = (data & 0xff000000) >> 24; int location = matrix.getIndex(x, y, z); matrix.data[location] = ColorRGBA8(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); } } else { i32 x = index % matrix.size.x; i32 y = index / matrix.size.x; index++; ui32 r = data & 0x000000ff; ui32 g = (data & 0x0000ff00) >> 8; ui32 b = (data & 0x00ff0000) >> 16; ui32 a = (data & 0xff000000) >> 24; int location = matrix.getIndex(x, y, z); matrix.data[location] = ColorRGBA8(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); } } z++; } } return ok; } ================================================ FILE: SoA/VoxelModelLoader.h ================================================ /// /// VoxelModelLoader.h /// Seed of Andromeda /// /// Created by Frank McCoy on 7 April 2015 /// Copyright 2014-2015 Regrowth Studios /// MIT License /// /// Summary: /// Class to handle loading of VoxelModels. /// Currently supports the Qubicle binary format. /// #pragma once #ifndef VoxelModelLoader_h__ #define VoxelModelLoader_h__ #include #include #define CODE_FLAG 2 #define NEXT_SLICE_FLAG 6 class VoxelMatrix; class VoxelModelLoader { public: static bool loadModel(const nString& filePath, VoxelMatrix& matrix); private: VoxelModelLoader(); }; #endif // VoxelModelLoader_h__ ================================================ FILE: SoA/VoxelModelMesh.cpp ================================================ #include "stdafx.h" #include "VoxelModelMesh.h" void VoxelModelMesh::bind() const { glBindVertexArray(m_vao); glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo); } void VoxelModelMesh::unbind() { glBindVertexArray(0); } void VoxelModelMesh::dispose() { if (m_vbo) { glDeleteBuffers(1, &m_vbo); m_vbo = 0; } if (m_ibo) { glDeleteBuffers(1, &m_ibo); m_ibo = 0; } if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; } } ================================================ FILE: SoA/VoxelModelMesh.h ================================================ #pragma once #ifndef VoxelModelMesh_h__ #define VoxelModelMesh_h__ #include #include class VoxelModelVertex { public: VoxelModelVertex() {} VoxelModelVertex(f32v3 pos, color3 color, f32v3 normal): pos(pos), normal(normal), color(color) {} f32v3 pos; f32v3 normal; color3 color; ui8 padding; }; class VoxelModelMesh { friend class ModelMesher; public: void bind() const; static void unbind(); void dispose(); VGVertexBuffer getVBO() const { return m_vbo; } VGIndexBuffer getIBO() const { return m_ibo; } VGVertexArray getVAO() const { return m_vao; } ui32 getIndexCount() const { return m_indCount; } ui32 getTriCount() const { return m_triCount; } private: VGVertexBuffer m_vbo = 0; VGIndexBuffer m_ibo = 0; VGVertexArray m_vao = 0; ui32 m_indCount = 0; ui32 m_triCount = 0; }; #endif // VoxelModelMesh_h__ ================================================ FILE: SoA/VoxelModelRenderer.cpp ================================================ #include "stdafx.h" #include "VoxelModelRenderer.h" #include "ShaderLoader.h" #include "VoxelMatrix.h" #include "VoxelModel.h" #include "VoxelModelMesh.h" #include "RenderUtils.h" #include void VoxelModelRenderer::initGL() { m_program = ShaderLoader::createProgramFromFile("Shaders/Models/VoxelModel.vert", "Shaders/Models/VoxelModel.frag"); } void VoxelModelRenderer::dispose() { if (m_program.isCreated()) m_program.dispose(); } void VoxelModelRenderer::draw(VoxelModelMesh* mesh, const f32m4& mVP, const f64v3& relativePos, const f64q& orientation) { // Convert f64q to f32q f32q orientationF32; orientationF32.x = (f32)orientation.x; orientationF32.y = (f32)orientation.y; orientationF32.z = (f32)orientation.z; orientationF32.w = (f32)orientation.w; // Convert to matrix f32m4 rotationMatrix = glm::toMat4(orientationF32); f32m4 mW(1.0); setMatrixTranslation(mW, -relativePos); f32m4 mWVP = mVP * mW * rotationMatrix; m_program.use(); glUniformMatrix4fv(m_program.getUniform("unWVP"), 1, false, &mWVP[0][0]); // TODO(Ben): Temporary f32v3 lightDir = glm::normalize(f32v3(1.0f, 0.0f, 1.0f)); glUniform3fv(m_program.getUniform("unLightDirWorld"), 1, &lightDir[0]); mesh->bind(); m_program.enableVertexAttribArrays(); glVertexAttribPointer(m_program.getAttribute("vPosition"), 3, GL_FLOAT, false, sizeof(VoxelModelVertex), offsetptr(VoxelModelVertex, pos)); glVertexAttribPointer(m_program.getAttribute("vNormal"), 3, GL_FLOAT, false, sizeof(VoxelModelVertex), offsetptr(VoxelModelVertex, normal)); glVertexAttribPointer(m_program.getAttribute("vColor"), 3, GL_UNSIGNED_BYTE, true, sizeof(VoxelModelVertex), offsetptr(VoxelModelVertex, color)); if (mesh->getIndexCount() == 0) { glDrawArrays(GL_TRIANGLES, 0, mesh->getTriCount() * 3); } else { glDrawElements(GL_TRIANGLES, mesh->getIndexCount(), GL_UNSIGNED_INT, nullptr); } mesh->unbind(); m_program.unuse(); } ================================================ FILE: SoA/VoxelModelRenderer.h ================================================ #pragma once #ifndef VoxelModelRenderer_h__ #define VoxelModelRenderer_h__ #include "Vorb/graphics/GLProgram.h" class VoxelMatrix; class VoxelModelMesh; class VoxelModelRenderer { public: void initGL(); void dispose(); void draw(VoxelModelMesh* mesh, const f32m4& mVP, const f64v3& relativePos, const f64q& orientation); private: vg::GLProgram m_program; }; #endif //VoxelModelRenderer_h__ ================================================ FILE: SoA/VoxelNavigation.inl ================================================ /// /// VoxelNavigation.inl /// Vorb /// /// Created by Benjamin Arnold on 26 Nov 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// This file implements the Voxel Navigation functions with /// thread safety. /// #pragma once #ifndef VoxelNavigation_inl__ #define VoxelNavigation_inl__ // TODO(Ben): Generalize Chunk and voxel stuff // TODO(Cristian): Try to tell Ben that we can't quite generalize everything... #include "Chunk.h" #include "VoxelUtils.h" namespace vorb { namespace voxel { // /// Locks a chunk and unlocks the currently locked chunk. Keeps track // /// of which chunk is locked. // /// @param chunkToLock: Chunk that needs to be locked // /// @param lockedChunk: Currently locked chunk, or nullptr if no chunk // /// is locked. Will be set to chunkToLock if chunkToLock is locked. // inline void swapLockedChunk(NChunk* chunkToLock, NChunk*& lockedChunk) { // if (chunkToLock == lockedChunk) return; // if (lockedChunk) lockedChunk->unlock(); // lockedChunk = chunkToLock; // lockedChunk->lock(); // } // // /// Gets the blockData to the left of the input block. Owner and index are stored. // inline int getLeftBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int x, int& nextBlockIndex, NChunk*& owner) { // if (x > 0) { // owner = chunk; // nextBlockIndex = blockIndex - 1; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->left && chunk->left->isAccessible) { // owner = chunk->left; // nextBlockIndex = blockIndex + CHUNK_WIDTH - 1; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the left of the input block. Owner and index are stored. // inline int getLeftBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getLeftBlockData(chunk, lockedChunk, blockIndex, getXFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the left of the input block. // inline int getLeftBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getXFromBlockIndex(blockIndex) > 0) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex - 1); // } else if (chunk->left && chunk->left->isAccessible) { // return chunk->left->getBlockDataSafe(lockedChunk, blockIndex + CHUNK_WIDTH - 1); // } // return -1; // } // /// Gets the blockData to the right of the input block. Owner and index are stored. // inline int getRightBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int x, int& nextBlockIndex, NChunk*& owner) { // if (x < CHUNK_WIDTH - 1) { // owner = chunk; // nextBlockIndex = blockIndex + 1; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->right && chunk->right->isAccessible) { // owner = chunk->right; // nextBlockIndex = blockIndex - CHUNK_WIDTH + 1; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the right of the input block. Owner and index are stored. // inline int getRightBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getRightBlockData(chunk, lockedChunk, blockIndex, getXFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the right of the input block. // inline int getRightBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getXFromBlockIndex(blockIndex) < CHUNK_WIDTH - 1) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex + 1); // } else if (chunk->right && chunk->right->isAccessible) { // return chunk->right->getBlockDataSafe(lockedChunk, blockIndex - CHUNK_WIDTH + 1); // } // return -1; // } // /// Gets the blockData to the front of the input block. Owner and index are stored. // inline int getFrontBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int z, int& nextBlockIndex, NChunk*& owner) { // if (z < CHUNK_WIDTH - 1) { // owner = chunk; // nextBlockIndex = blockIndex + CHUNK_WIDTH; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->front && chunk->front->isAccessible) { // owner = chunk->front; // nextBlockIndex = blockIndex - CHUNK_LAYER + CHUNK_WIDTH; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the front of the input block. Owner and index are stored. // inline int getFrontBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getFrontBlockData(chunk, lockedChunk, blockIndex, getZFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the front of the input block. // inline int getFrontBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getZFromBlockIndex(blockIndex) < CHUNK_WIDTH - 1) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex + CHUNK_WIDTH); // } else if (chunk->front && chunk->front->isAccessible) { // return chunk->front->getBlockDataSafe(lockedChunk, blockIndex - CHUNK_LAYER + CHUNK_WIDTH); // } // return -1; // } // /// Gets the blockData to the back of the input block. Owner and index are stored. // inline int getBackBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int z, int& nextBlockIndex, NChunk*& owner) { // if (z > 0) { // owner = chunk; // nextBlockIndex = blockIndex - CHUNK_WIDTH; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->back && chunk->back->isAccessible) { // owner = chunk->back; // nextBlockIndex = blockIndex + CHUNK_LAYER - CHUNK_WIDTH; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the back of the input block. Owner and index are stored. // inline int getBackBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getBackBlockData(chunk, lockedChunk, blockIndex, getZFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the back of the input block. // inline int getBackBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getZFromBlockIndex(blockIndex) > 0) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex - CHUNK_WIDTH); // } else if (chunk->back && chunk->back->isAccessible) { // return chunk->back->getBlockDataSafe(lockedChunk, blockIndex + CHUNK_LAYER - CHUNK_WIDTH); // } // return -1; // } // /// Gets the blockData to the bottom of the input block. Owner and index are stored. // inline int getBottomBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int y, int& nextBlockIndex, NChunk*& owner) { // if (y > 0) { // owner = chunk; // nextBlockIndex = blockIndex - CHUNK_LAYER; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->bottom && chunk->bottom->isAccessible) { // owner = chunk->bottom; // nextBlockIndex = blockIndex + CHUNK_SIZE - CHUNK_LAYER; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the bottom of the input block. Owner and index are stored. // inline int getBottomBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getBottomBlockData(chunk, lockedChunk, blockIndex, getYFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the bottom of the input block. // inline int getBottomBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getYFromBlockIndex(blockIndex) > 0) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex - CHUNK_LAYER); // } else if (chunk->bottom && chunk->bottom->isAccessible) { // return chunk->bottom->getBlockDataSafe(lockedChunk, blockIndex + CHUNK_SIZE - CHUNK_LAYER); // } // return -1; // } // /// Gets the blockData to the top of the input block. Owner and index are stored. // inline int getTopBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int y, int& nextBlockIndex, NChunk*& owner) { // if (y < CHUNK_WIDTH - 1) { // owner = chunk; // nextBlockIndex = blockIndex + CHUNK_LAYER; // return chunk->getBlockDataSafe(lockedChunk, nextBlockIndex); // } else if (chunk->top && chunk->top->isAccessible) { // owner = chunk->top; // nextBlockIndex = blockIndex - CHUNK_SIZE + CHUNK_LAYER; // return owner->getBlockDataSafe(lockedChunk, nextBlockIndex); // } // return -1; // } // /// Gets the blockData to the top of the input block. Owner and index are stored. // inline int getTopBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex, int& nextBlockIndex, NChunk*& owner) { // return getTopBlockData(chunk, lockedChunk, blockIndex, getYFromBlockIndex(blockIndex), nextBlockIndex, owner); // } // /// Gets the blockData to the top of the input block. // inline int getTopBlockData(NChunk* chunk, NChunk*& lockedChunk, int blockIndex) { // if (getYFromBlockIndex(blockIndex) < CHUNK_WIDTH - 1) { // return chunk->getBlockDataSafe(lockedChunk, blockIndex + CHUNK_LAYER); // } else if (chunk->top && chunk->top->isAccessible) { // return chunk->top->getBlockDataSafe(lockedChunk, blockIndex - CHUNK_SIZE + CHUNK_LAYER); // } // return -1; // } } } namespace vvox = vorb::voxel; #endif // VoxelNavigation_inl__ ================================================ FILE: SoA/VoxelNodeSetter.cpp ================================================ #include "stdafx.h" #include "VoxelNodeSetter.h" #include "ChunkGrid.h" void VoxelNodeSetter::setNodes(ChunkHandle& h, ChunkGenLevel requiredGenLevel, std::vector& forcedNodes, std::vector& condNodes) { { std::lock_guard l(m_lckVoxelsToAdd); auto it = m_handleLookup.find(h); if (it != m_handleLookup.end()) { VoxelNodeSetterLookupData& ld = it->second; // Copy new voxels to add size_t oldSize = ld.forcedNodes.size(); ld.forcedNodes.resize(oldSize + forcedNodes.size()); memcpy(&ld.forcedNodes[oldSize], forcedNodes.data(), forcedNodes.size() * sizeof(VoxelToPlace)); oldSize = ld.condNodes.size(); ld.condNodes.resize(oldSize + condNodes.size()); memcpy(&ld.condNodes[oldSize], condNodes.data(), condNodes.size() * sizeof(VoxelToPlace)); // Update required gen level if needed if (m_waitingChunks[ld.waitingChunksIndex].requiredGenLevel < requiredGenLevel) { m_waitingChunks[ld.waitingChunksIndex].requiredGenLevel = requiredGenLevel; } } else { VoxelNodeSetterLookupData& ld = m_handleLookup[h]; ld.forcedNodes.swap(forcedNodes); ld.condNodes.swap(condNodes); ld.h = h.acquire(); ld.waitingChunksIndex = m_waitingChunks.size(); VoxelNodeSetterWaitingChunk wc; wc.ch = h; wc.requiredGenLevel = requiredGenLevel; m_waitingChunks.push_back(std::move(wc)); } } // TODO(Ben): Faster overload? grid->submitQuery(h->getChunkPosition(), requiredGenLevel, true); } void VoxelNodeSetter::update() { if (m_waitingChunks.size()) { std::cout << m_waitingChunks.size() << std::endl; } std::lock_guard l(m_lckVoxelsToAdd); for (int i = (int)m_waitingChunks.size() - 1; i >= 0; i--) { VoxelNodeSetterWaitingChunk& v = m_waitingChunks[i]; if (v.ch->genLevel >= v.requiredGenLevel) { auto it = m_handleLookup.find(v.ch); { // Send task // TODO(Ben): MEMORY MANAGEMENT PROBLEM VoxelNodeSetterTask* newTask = new VoxelNodeSetterTask; newTask->h = it->second.h.acquire(); newTask->forcedNodes.swap(it->second.forcedNodes); newTask->condNodes.swap(it->second.condNodes); threadPool->addTask(newTask); } it->second.h.release(); m_handleLookup.erase(it); // Copy back node for fast removal from vector VoxelNodeSetterWaitingChunk& bn = m_waitingChunks.back(); v.ch = bn.ch; v.requiredGenLevel = bn.requiredGenLevel; // Update lookup index m_handleLookup[v.ch].waitingChunksIndex = i; m_waitingChunks.pop_back(); } } } ================================================ FILE: SoA/VoxelNodeSetter.h ================================================ // // VoxelNodeSetter.h // Seed of Andromeda // // Created by Benjamin Arnold on 9 Sep 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Waits on chunk generation queries and then sets voxels. // #pragma once #ifndef VoxelNodeSetter_h__ #define VoxelNodeSetter_h__ #include #include "ChunkQuery.h" #include "VoxelNodeSetterTask.h" class ChunkHandle; class ChunkGrid; struct VoxelNodeSetterWaitingChunk { Chunk* ch; ChunkGenLevel requiredGenLevel; }; struct VoxelNodeSetterLookupData { ui32 waitingChunksIndex; ChunkHandle h; std::vector forcedNodes; ///< Always added std::vector condNodes; ///< Conditionally added }; class VoxelNodeSetter { public: // Contents of vectors may be cleared void setNodes(ChunkHandle& h, ChunkGenLevel requiredGenLevel, std::vector& forcedNodes, std::vector& condNodes); void update(); ChunkGrid* grid = nullptr; vcore::ThreadPool* threadPool; private: std::mutex m_lckVoxelsToAdd; std::vector m_waitingChunks; std::map m_handleLookup; ///< Stores handles since they fk up in vector. }; #endif // VoxelNodeSetter_h__ ================================================ FILE: SoA/VoxelNodeSetterTask.cpp ================================================ #include "stdafx.h" #include "VoxelNodeSetterTask.h" #include "ChunkHandle.h" #include "Chunk.h" void VoxelNodeSetterTask::execute(WorkerData* workerData VORB_MAYBE_UNUSED) { { std::lock_guard l(h->dataMutex); for (auto& node : forcedNodes) { h->blocks.set(node.blockIndex, node.blockID); } for (auto& node : condNodes) { // TODO(Ben): Custom condition if (h->blocks.get(node.blockIndex) == 0) { h->blocks.set(node.blockIndex, node.blockID); } } } if (h->genLevel >= GEN_DONE) h->DataChange(h); h.release(); } void VoxelNodeSetterTask::cleanup() { // TODO(Ben): Better memory management. delete this; } ================================================ FILE: SoA/VoxelNodeSetterTask.h ================================================ // // VoxelNodeSetterTask.h // Seed of Andromeda // // Created by Benjamin Arnold on 10 Sep 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // A task for setting voxels using the threadpool. // #pragma once #ifndef VoxelNodeSetterTask_h__ #define VoxelNodeSetterTask_h__ #include #include "ChunkHandle.h" class WorkerData; struct VoxelToPlace { VoxelToPlace() {}; VoxelToPlace(ui16 blockID, ui16 blockIndex) : blockID(blockID), blockIndex(blockIndex) {}; ui16 blockID; ui16 blockIndex; }; class VoxelNodeSetterTask : public vcore::IThreadPoolTask { public: // Executes the task void execute(WorkerData* workerData) override; void cleanup() override; ChunkHandle h; std::vector forcedNodes; ///< Always added std::vector condNodes; ///< Conditionally added }; #endif // VoxelNodeSetterTask_h__ ================================================ FILE: SoA/VoxelRay.cpp ================================================ #include "stdafx.h" #include "VoxelRay.h" #include #include inline f64 fastFloorf(f64 x) { return FastConversion::floor(x); } inline f64 fastCeilf(f64 x) { return FastConversion::ceiling(x); } VoxelRay::VoxelRay(f64v3 start, f64v3 direction) { _startPos = start; _direction = direction; _currentPos = _startPos; _currentVoxelPos = i32v3(fastFloor(_startPos.x), fastFloor(_startPos.y), fastFloor(_startPos.z)); _currentDist = 0.0f; } i32v3 VoxelRay::getNextVoxelPosition() { //Find All Distances To Next Voxel In Each Direction f64v3 next; f64v3 r; // X-Distance if (_direction.x > 0) { if (_currentPos.x == (i32)_currentPos.x) next.x = _currentPos.x + 1; else next.x = fastCeilf(_currentPos.x); r.x = (next.x - _currentPos.x) / _direction.x; } else if (_direction.x < 0) { if (_currentPos.x == (i32)_currentPos.x) next.x = _currentPos.x - 1; else next.x = fastFloorf(_currentPos.x); r.x = (next.x - _currentPos.x) / _direction.x; } else { r.x = FLT_MAX; } // Y-Distance if (_direction.y > 0) { if (_currentPos.y == (i32)_currentPos.y) next.y = _currentPos.y + 1; else next.y = fastCeilf(_currentPos.y); r.y = (next.y - _currentPos.y) / _direction.y; } else if (_direction.y < 0) { if (_currentPos.y == (i32)_currentPos.y) next.y = _currentPos.y - 1; else next.y = fastFloorf(_currentPos.y); r.y = (next.y - _currentPos.y) / _direction.y; } else { r.y = FLT_MAX; } // Z-Distance if (_direction.z > 0) { if (_currentPos.z == (i32)_currentPos.z) next.z = _currentPos.z + 1; else next.z = fastCeilf(_currentPos.z); r.z = (next.z - _currentPos.z) / _direction.z; } else if (_direction.z < 0) { if (_currentPos.z == (i32)_currentPos.z) next.z = _currentPos.z - 1; else next.z = fastFloorf(_currentPos.z); r.z = (next.z - _currentPos.z) / _direction.z; } else { r.z = FLT_MAX; } // Get Minimum Movement To The Next Voxel f64 rat; if (r.x < r.y && r.x < r.z) { // Move In The X-Direction rat = r.x; _currentPos += _direction * rat; if (_direction.x > 0) _currentVoxelPos.x++; else if (_direction.x < 0) _currentVoxelPos.x--; } else if (r.y < r.z) { // Move In The Y-Direction rat = r.y; _currentPos += _direction * rat; if (_direction.y > 0) _currentVoxelPos.y++; else if (_direction.y < 0) _currentVoxelPos.y--; } else { // Move In The Z-Direction rat = r.z; _currentPos += _direction * rat; if (_direction.z > 0) _currentVoxelPos.z++; else if (_direction.z < 0) _currentVoxelPos.z--; } // Add The Distance The Ray Has Traversed _currentDist += rat; return _currentVoxelPos; } ================================================ FILE: SoA/VoxelRay.h ================================================ #pragma once #include // Traverses The Endless Space Of Local Voxels class VoxelRay { public: // Create A Ray At The Starting Local Position With A Normalized Direction VoxelRay(f64v3 start, f64v3 direction); // Traverse To The Next Voxel And Return The Local Coordinates (Grid Offset) i32v3 getNextVoxelPosition(); // Access The Origin Values const f64v3& getStartPosition() const { return _startPos; } const f64v3& getDirection() const { return _direction; } // The Total Distance The Ray Has Traversed const f64& getDistanceTraversed() const { return _currentDist; } private: // Initialization Values f64v3 _startPos; f64v3 _direction; // The Current Traversal Information f64 _currentDist; f64v3 _currentPos; // The Offset From The Chunk Grid Origin i32v3 _currentVoxelPos; }; ================================================ FILE: SoA/VoxelSpaceConversions.cpp ================================================ #include "stdafx.h" #include "VoxelSpaceConversions.h" #include f32v3 VoxelSpaceConversions::getCoordinateMults(const ChunkPosition2D& facePosition) { f32v3 rv; rv.x = (float)FACE_TO_WORLD_MULTS[facePosition.face].x; rv.y = (float)FACE_Y_MULTS[facePosition.face]; rv.z = (float)FACE_TO_WORLD_MULTS[facePosition.face].y; return rv; } f32v3 VoxelSpaceConversions::getCoordinateMults(const ChunkPosition3D& facePosition) { f32v3 rv; rv.x = (float)FACE_TO_WORLD_MULTS[facePosition.face].x; rv.y = (float)FACE_Y_MULTS[facePosition.face]; rv.z = (float)FACE_TO_WORLD_MULTS[facePosition.face].y; return rv; } i32v3 VoxelSpaceConversions::getCoordinateMapping(const ChunkPosition2D& facePosition) { return VOXEL_TO_WORLD[facePosition.face]; } i32v3 VoxelSpaceConversions::getCoordinateMapping(const ChunkPosition3D& facePosition) { return VOXEL_TO_WORLD[facePosition.face]; } ChunkPosition2D VoxelSpaceConversions::voxelToChunk(const VoxelPosition2D& voxelPosition) { ChunkPosition2D gpos; gpos.face = voxelPosition.face; gpos.pos.x = fastFloor(voxelPosition.pos.x / CHUNK_WIDTH); gpos.pos.y = fastFloor(voxelPosition.pos.y / CHUNK_WIDTH); return gpos; } ChunkPosition3D VoxelSpaceConversions::voxelToChunk(const VoxelPosition3D& voxelPosition) { ChunkPosition3D gpos; gpos.face = voxelPosition.face; gpos.pos.x = fastFloor(voxelPosition.pos.x / CHUNK_WIDTH); gpos.pos.y = fastFloor(voxelPosition.pos.y / CHUNK_WIDTH); gpos.pos.z = fastFloor(voxelPosition.pos.z / CHUNK_WIDTH); return gpos; } i32v3 VoxelSpaceConversions::voxelToChunk(const i32v3& voxelPosition) { i32v3 gpos; gpos.x = fastFloor(voxelPosition.x / (float)CHUNK_WIDTH); gpos.y = fastFloor(voxelPosition.y / (float)CHUNK_WIDTH); gpos.z = fastFloor(voxelPosition.z / (float)CHUNK_WIDTH); return gpos; } i32v3 VoxelSpaceConversions::voxelToChunk(const f64v3& voxelPosition) { i32v3 gpos; gpos.x = fastFloor(voxelPosition.x / CHUNK_WIDTH); gpos.y = fastFloor(voxelPosition.y / CHUNK_WIDTH); gpos.z = fastFloor(voxelPosition.z / CHUNK_WIDTH); return gpos; } VoxelPosition2D VoxelSpaceConversions::chunkToVoxel(const ChunkPosition2D& gridPosition) { VoxelPosition2D vpos; vpos.face = gridPosition.face; vpos.pos.x = gridPosition.pos.x * CHUNK_WIDTH; vpos.pos.y = gridPosition.pos.y * CHUNK_WIDTH; return vpos; } VoxelPosition3D VoxelSpaceConversions::chunkToVoxel(const ChunkPosition3D& gridPosition) { VoxelPosition3D vpos; vpos.face = gridPosition.face; vpos.pos.x = gridPosition.pos.x * CHUNK_WIDTH; vpos.pos.y = gridPosition.pos.y * CHUNK_WIDTH; vpos.pos.z = gridPosition.pos.z * CHUNK_WIDTH; return vpos; } f64v3 VoxelSpaceConversions::voxelToWorld(const VoxelPosition2D& facePosition, f64 voxelWorldRadius) { return voxelToWorldNormalized(facePosition, voxelWorldRadius) * voxelWorldRadius; } f64v3 VoxelSpaceConversions::voxelToWorld(const VoxelPosition3D& facePosition, f64 voxelWorldRadius) { return voxelToWorldNormalized(facePosition, voxelWorldRadius) * (voxelWorldRadius + facePosition.pos.y); } f64v3 VoxelSpaceConversions::chunkToWorld(const ChunkPosition2D& facePosition, f64 voxelWorldRadius) { return chunkToWorldNormalized(facePosition, voxelWorldRadius) * voxelWorldRadius; } f64v3 VoxelSpaceConversions::chunkToWorld(const ChunkPosition3D& facePosition, f64 voxelWorldRadius) { return chunkToWorldNormalized(facePosition, voxelWorldRadius) * (voxelWorldRadius + facePosition.pos.y * CHUNK_WIDTH); } f64v3 VoxelSpaceConversions::voxelToWorldNormalized(const VoxelPosition2D& facePosition, f64 voxelWorldRadius) { const i32v3& axisMapping = VOXEL_TO_WORLD[facePosition.face]; const i32v2& mults = FACE_TO_WORLD_MULTS[facePosition.face]; f64v3 worldPosition; worldPosition[axisMapping.x] = facePosition.pos.x * mults.x; worldPosition[axisMapping.y] = voxelWorldRadius * FACE_Y_MULTS[facePosition.face]; worldPosition[axisMapping.z] = facePosition.pos.y * mults.y; return glm::normalize(worldPosition); } f64v3 VoxelSpaceConversions::voxelToWorldNormalized(const VoxelPosition3D& facePosition, f64 voxelWorldRadius) { const i32v3& axisMapping = VOXEL_TO_WORLD[facePosition.face]; const i32v2& mults = FACE_TO_WORLD_MULTS[facePosition.face]; f64v3 worldPosition; worldPosition[axisMapping.x] = facePosition.pos.x * mults.x; worldPosition[axisMapping.y] = voxelWorldRadius * FACE_Y_MULTS[facePosition.face]; worldPosition[axisMapping.z] = facePosition.pos.z * mults.y; return glm::normalize(worldPosition); } f64v3 VoxelSpaceConversions::chunkToWorldNormalized(const ChunkPosition2D& facePosition, f64 voxelWorldRadius) { return voxelToWorldNormalized(chunkToVoxel(facePosition), voxelWorldRadius); } f64v3 VoxelSpaceConversions::chunkToWorldNormalized(const ChunkPosition3D& facePosition, f64 voxelWorldRadius) { return voxelToWorldNormalized(chunkToVoxel(facePosition), voxelWorldRadius); } VoxelPosition3D computeGridPosition(const f32v3& hitpoint, f32 radius) { f32v3 cornerPos[2]; f32 min; f32v3 start = glm::normalize(hitpoint) * 2.0f * radius; f32v3 dir = -glm::normalize(hitpoint); cornerPos[0] = f32v3(-radius, -radius, -radius); cornerPos[1] = f32v3(radius, radius, radius); if (!IntersectionUtils::boxIntersect(cornerPos, dir, start, min)) { std::cerr << "ERROR: Failed to find grid position in computeGridPosition.\n"; } f32v3 gridHit = start + dir * min; const f32 eps = 32.0f; VoxelPosition3D gridPos; if (abs(gridHit.x - (-radius)) < eps) { //-X gridPos.face = WorldCubeFace::FACE_LEFT; gridPos.pos.x = gridHit.z; gridPos.pos.z = -gridHit.y; } else if (abs(gridHit.x - radius) < eps) { //X gridPos.face = WorldCubeFace::FACE_RIGHT; gridPos.pos.x = -gridHit.z; gridPos.pos.z = -gridHit.y; } else if (abs(gridHit.y - (-radius)) < eps) { //-Y gridPos.face = WorldCubeFace::FACE_BOTTOM; gridPos.pos.x = gridHit.x; gridPos.pos.z = -gridHit.z; } else if (abs(gridHit.y - radius) < eps) { //Y gridPos.face = WorldCubeFace::FACE_TOP; gridPos.pos.x = gridHit.x; gridPos.pos.z = gridHit.z; } else if (abs(gridHit.z - (-radius)) < eps) { //-Z gridPos.face = WorldCubeFace::FACE_BACK; gridPos.pos.x = -gridHit.x; gridPos.pos.z = -gridHit.y; } else if (abs(gridHit.z - radius) < eps) { //Z gridPos.face = WorldCubeFace::FACE_FRONT; gridPos.pos.x = gridHit.x; gridPos.pos.z = -gridHit.y; } else { std::cerr << "ERROR: Failed to pick voxel position in computeGridPosition.\n"; } return gridPos; } VoxelPosition3D VoxelSpaceConversions::worldToVoxel(const f64v3& worldPosition, f64 voxelWorldRadius) { f64v3 wpoint = glm::normalize(worldPosition) * voxelWorldRadius * 2.0; // Compute the intersection f32v3 normal, hitpoint; f32 distance; VoxelPosition3D gridPos; if (IntersectionUtils::sphereIntersect(-f32v3(glm::normalize(wpoint)), f32v3(wpoint), f32v3(0.0f), (f32)voxelWorldRadius, hitpoint, distance, normal)) { // Compute face and grid position gridPos = computeGridPosition(hitpoint, (f32)voxelWorldRadius); gridPos.pos.y = glm::length(worldPosition) - voxelWorldRadius; } return gridPos; } ================================================ FILE: SoA/VoxelSpaceConversions.h ================================================ /// /// VoxelSpaceConversions.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 27 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Converts between the different voxel coordinate spaces /// #pragma once #ifndef VoxelSpaceConversions_h__ #define VoxelSpaceConversions_h__ #include "Constants.h" #include "VoxelCoordinateSpaces.h" #include /// Namespace for converting between the different voxel and chunk coordinate spaces namespace VoxelSpaceConversions { #define W_X 0 #define W_Y 1 #define W_Z 2 /// Maps face-space coordinate axis to world-space coordinates. /// [face] const i32v3 VOXEL_TO_WORLD[6] = { i32v3(W_X, W_Y, W_Z), // TOP i32v3(W_Z, W_X, W_Y), // LEFT i32v3(W_Z, W_X, W_Y), // RIGHT i32v3(W_X, W_Z, W_Y), // FRONT i32v3(W_X, W_Z, W_Y), // BACK i32v3(W_X, W_Y, W_Z) }; // BOTTOM #undef W_X #undef W_Y #undef W_Z /// Multiply by the face-space Y axis in order to get the correct direction /// for its corresponding world space axis /// [face] const int FACE_Y_MULTS[6] = { 1, -1, 1, 1, -1, -1 }; /// Multiply by the voxel-space X,Z axis in order to get the correct direction /// for its corresponding world-space axis /// [face] const i32v2 FACE_TO_WORLD_MULTS[6] = { i32v2(1, 1), // TOP i32v2(1, -1), // LEFT i32v2(-1, -1), // RIGHT i32v2(1, -1), // FRONT i32v2(-1, -1), // BACK i32v2(1, -1) }; // BOTTOM /// Gets multipliers for converting face direction to world direction extern f32v3 getCoordinateMults(const ChunkPosition2D& facePosition); extern f32v3 getCoordinateMults(const ChunkPosition3D& facePosition); /// Gets coordinate mappings for converting face position to world position extern i32v3 getCoordinateMapping(const ChunkPosition2D& facePosition); extern i32v3 getCoordinateMapping(const ChunkPosition3D& facePosition); /// Converts from voxel-space to chunk-space /// Does not affect rotation or face /// @param voxelPosition: The voxel grid position /// @return the chunk grid position extern ChunkPosition2D voxelToChunk(const VoxelPosition2D& voxelPosition); extern ChunkPosition3D voxelToChunk(const VoxelPosition3D& voxelPosition); extern i32v3 voxelToChunk(const i32v3& voxelPosition); extern i32v3 voxelToChunk(const f64v3& voxelPosition); /// Converts from chunk-space to voxel-space /// Does not affect rotation or face /// @param gridPosition: The chunk grid position /// @return the voxel position extern VoxelPosition2D chunkToVoxel(const ChunkPosition2D& gridPosition); extern VoxelPosition3D chunkToVoxel(const ChunkPosition3D& gridPosition); /// Converts from face-space to world-space /// @param facePosition: The face position /// @param voxelWorldRadius: Radius of the world in units of voxels /// @return the world position extern f64v3 voxelToWorld(const VoxelPosition2D& facePosition, f64 voxelWorldRadius); extern f64v3 voxelToWorld(const VoxelPosition3D& facePosition, f64 voxelWorldRadius); /// Converts from face-space to world-space /// @param facePosition: The face position /// @param voxelWorldRadius: Radius of the world in units of voxels /// @return the world position extern f64v3 chunkToWorld(const ChunkPosition2D& facePosition, f64 voxelWorldRadius); extern f64v3 chunkToWorld(const ChunkPosition3D& facePosition, f64 voxelWorldRadius); /// Converts from face-space to normalized world-space /// @param facePosition: The face position /// @param voxelWorldRadius: Radius of the world in units of voxels /// @return the normalized world position extern f64v3 voxelToWorldNormalized(const VoxelPosition2D& facePosition, f64 voxelWorldRadius); extern f64v3 voxelToWorldNormalized(const VoxelPosition3D& facePosition, f64 voxelWorldRadius); /// Converts from face-space to normalized world-space /// @param facePosition: The face position /// @param voxelWorldRadius: Radius of the world in units of voxels /// @return the normalized world position extern f64v3 chunkToWorldNormalized(const ChunkPosition2D& facePosition, f64 voxelWorldRadius); extern f64v3 chunkToWorldNormalized(const ChunkPosition3D& facePosition, f64 voxelWorldRadius); /// Converts from world-space to unrotated voxel-space /// @param worldSpace: World position in units of voxels. @pre should rotated so that the planet has /// a relative orientation of 0 /// @return the voxel position extern VoxelPosition3D worldToVoxel(const f64v3& worldPosition, f64 voxelWorldRadius); } #endif // VoxelSpaceConversions_h__ ================================================ FILE: SoA/VoxelSpaceUtils.cpp ================================================ #include "stdafx.h" #include "VoxelSpaceUtils.h" #include "VoxelSpaceConversions.h" /// Defines the effect that transitioning from face i to face j will have on /// rotation. Simply add to rotation value modulo 4 /// Each rotation represents a clockwise turn. /// [source][destination] // const int FACE_TRANSITIONS[6][6] = { // { 0, -1, 1, 0, 2, 0 }, // TOP // { 1, 0, 0, 0, 0, -1 }, // LEFT // { -1, 0, 0, 0, 0, 1 }, // RIGHT // { 0, 0, 0, 0, 0, 0 }, // FRONT // { 2, 0, 0, 0, 0, 2 }, // BACK // { 0, 1, -1, 0, 2, 0 } }; // BOTTOM /// Neighbors, starting from +x and moving clockwise /// [face][rotation] // const WorldCubeFace FACE_NEIGHBORS[6][4] = { // { FACE_RIGHT, FACE_FRONT, FACE_LEFT, FACE_BACK }, // TOP // { FACE_FRONT, FACE_BOTTOM, FACE_BACK, FACE_TOP }, // LEFT // { FACE_BACK, FACE_BOTTOM, FACE_FRONT, FACE_TOP }, // RIGHT // { FACE_RIGHT, FACE_BOTTOM, FACE_LEFT, FACE_TOP }, // FRONT // { FACE_LEFT, FACE_BOTTOM, FACE_RIGHT, FACE_TOP }, // BACK // { FACE_RIGHT, FACE_BACK, FACE_LEFT, FACE_FRONT } }; // BOTTOM //f64q q1 = quatBetweenVectors(FACE_NORMALS[0], VoxelSpaceConversions::voxelFaceToWorldNormalized( // VoxelSpaceConversions::voxelGridToFace(gridPosition), worldRadius)); //f64q q2 = quatBetweenVectors(f32v3(1.0, 0.0, 0.0), q1 * f32v3(1.0, 0.0, 0.0)) #define OFFSET 1.0 /// This is basically a hack with normal mapping tangent space stuff f64q VoxelSpaceUtils::calculateVoxelToSpaceQuat(const VoxelPosition2D& gridPosition, f64 worldRadius) { VoxelPosition2D gp2 = gridPosition; gp2.pos.x += OFFSET; VoxelPosition2D gp3 = gridPosition; gp3.pos.y += OFFSET; f64v3 v1 = VoxelSpaceConversions::voxelToWorldNormalized( gridPosition, worldRadius); f64v3 v2 = VoxelSpaceConversions::voxelToWorldNormalized( gp2, worldRadius); f64v3 v3 = VoxelSpaceConversions::voxelToWorldNormalized( gp3, worldRadius); f64v3 tangent = glm::normalize(v2 - v1); f64v3 biTangent = glm::normalize(v3 - v1); f64m4 worldRotationMatrix; worldRotationMatrix[0] = f64v4(tangent, 0); worldRotationMatrix[1] = f64v4(v1, 0); worldRotationMatrix[2] = f64v4(biTangent, 0); worldRotationMatrix[3] = f64v4(0, 0, 0, 1); return glm::quat_cast(worldRotationMatrix); } f64q VoxelSpaceUtils::calculateVoxelToSpaceQuat(const VoxelPosition3D& gridPosition, f64 worldRadius) { VoxelPosition3D gp2 = gridPosition; gp2.pos.x += OFFSET; f64v3 v1 = VoxelSpaceConversions::voxelToWorldNormalized( gridPosition, worldRadius); f64v3 v2 = VoxelSpaceConversions::voxelToWorldNormalized( gp2, worldRadius); f64v3 tangent = glm::normalize(v2 - v1); f64v3 biTangent = glm::cross(tangent, v1); f64m4 worldRotationMatrix; worldRotationMatrix[0] = f64v4(tangent, 0); worldRotationMatrix[1] = f64v4(v1, 0); worldRotationMatrix[2] = f64v4(biTangent, 0); worldRotationMatrix[3] = f64v4(0, 0, 0, 1); return glm::quat_cast(worldRotationMatrix); } void VoxelSpaceUtils::offsetChunkGridPosition(OUT ChunkPosition2D& gridPosition, const i32v2& xzOffset, int maxPos VORB_UNUSED) { gridPosition.pos += xzOffset; WorldCubeFace newFace = gridPosition.face; //TODO(Ben): New transition logic and then remove VORB_UNUSED tags. //if (gridPosition.pos.y < -maxPos) { // BOTTOM SIDE // gridPosition.pos.y += maxPos; // newFace = FACE_NEIGHBORS[gridPosition.face][(1 + gridPosition.rotation) % 4]; // gridPosition.rotation += FACE_TRANSITIONS[gridPosition.face][newFace]; // if (gridPosition.rotation < 0) { gridPosition.rotation += 4; } else { gridPosition.rotation %= 4; } //} else if (gridPosition.pos.y > maxPos) { // TOP SIDE // gridPosition.pos.y -= maxPos; // newFace = FACE_NEIGHBORS[gridPosition.face][(3 + gridPosition.rotation) % 4]; // gridPosition.rotation += FACE_TRANSITIONS[gridPosition.face][newFace]; // if (gridPosition.rotation < 0) { gridPosition.rotation += 4; } else { gridPosition.rotation %= 4; } //} //if (gridPosition.pos.x < -maxPos) { // LEFT SIDE // gridPosition.pos.x += maxPos; // newFace = FACE_NEIGHBORS[gridPosition.face][(2 + gridPosition.rotation) % 4]; // gridPosition.rotation += FACE_TRANSITIONS[gridPosition.face][newFace]; // if (gridPosition.rotation < 0) { gridPosition.rotation += 4; } else { gridPosition.rotation %= 4; } //} else if (gridPosition.pos.x > maxPos) { // RIGHT SIDE // gridPosition.pos.x -= maxPos; // newFace = FACE_NEIGHBORS[gridPosition.face][gridPosition.rotation]; // gridPosition.rotation += FACE_TRANSITIONS[gridPosition.face][newFace]; // if (gridPosition.rotation < 0) { gridPosition.rotation += 4; } else { gridPosition.rotation %= 4; } //} gridPosition.face = newFace; } ================================================ FILE: SoA/VoxelSpaceUtils.h ================================================ /// /// VoxelSpaceUtils.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 27 Jan 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Utilities for voxel coordinate space operations /// #pragma once #ifndef VoxelSpaceUtils_h__ #define VoxelSpaceUtils_h__ #include "VoxelCoordinateSpaces.h" namespace VoxelSpaceUtils { /// Calculates the quaternion that can convert grid orientation to world orientation /// @param gridPosition: Voxel grid position /// @param worldRadius: Radius of the world in voxels /// @return quaternion that describes the relative rotation extern f64q calculateVoxelToSpaceQuat(const VoxelPosition2D& gridPosition, f64 worldRadius); extern f64q calculateVoxelToSpaceQuat(const VoxelPosition3D& gridPosition, f64 worldRadius); /// Offsets a chunk grid position and handles rotation /// @param gridPosition: The chunk grid position /// @param xzOffset: The offset to apply, in chunks /// @param maxPos: Maximum grid position, for + and - direction extern void offsetChunkGridPosition(ChunkPosition2D& gridPosition, const i32v2& xzOffset, int maxPos); // TODO(Ben): implement this with new stuff /* void getIterationConstants(OUT int& jStart, OUT int& jMult, OUT int& jEnd, OUT int& jInc, OUT int& kStart, OUT int& kMult, OUT int& kEnd, OUT int& kInc) { switch (rotation) { //we use rotation value to un-rotate the chunk data case 0: //no rotation jStart = 0; kStart = 0; jEnd = kEnd = CHUNK_WIDTH; jInc = kInc = 1; jMult = CHUNK_WIDTH; kMult = 1; break; case 1: //up is right jMult = 1; jStart = CHUNK_WIDTH - 1; jEnd = -1; jInc = -1; kStart = 0; kEnd = CHUNK_WIDTH; kInc = 1; kMult = CHUNK_WIDTH; break; case 2: //up is down jMult = CHUNK_WIDTH; jStart = CHUNK_WIDTH - 1; kStart = CHUNK_WIDTH - 1; jEnd = kEnd = -1; jInc = kInc = -1; kMult = 1; break; case 3: //up is left jMult = 1; jStart = 0; jEnd = CHUNK_WIDTH; jInc = 1; kMult = CHUNK_WIDTH; kStart = CHUNK_WIDTH - 1; kEnd = -1; kInc = -1; break; } } */ } #endif // VoxelSpaceUtils_h__ ================================================ FILE: SoA/VoxelUpdateBufferer.h ================================================ // // VoxelUpdateBuffer.h // Seed of Andromeda // // Created by Benjamin Arnold on 10 Sep 2015 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // Buffers voxel updates and applies all at once for minimal locking. // #pragma once #ifndef VoxelUpdateBuffer_h__ #define VoxelUpdateBuffer_h__ #include "ChunkHandle.h" #include "Chunk.h" #include struct VoxelUpdateBuffer { inline void finish() { h.release(); } ui8 bits[CHUNK_SIZE / 8]; ///< To check if an id was already added std::vector toUpdate; ChunkHandle h; }; #define DIV_8_SHIFT 3 #define MOD_8_AND 0x5 class VoxelUpdateBufferer { public: void reserve(std::unordered_map::size_type sizeApprox) { buffers.reserve(sizeApprox); } inline void addUpdate(ChunkHandle chunk, BlockIndex index) { auto it = buffers.find(chunk->getID()); if (it == buffers.end()) { // TODO(Ben): Verify that there is no extra copy. VoxelUpdateBuffer nbuff = {}; VoxelUpdateBuffer& b = buffers.emplace(chunk->getID(), std::move(nbuff)).first->second; // Set the bit b.bits[index >> DIV_8_SHIFT] |= (1 << (index & MOD_8_AND)); b.toUpdate.push_back(index); b.h = chunk.acquire(); } else { VoxelUpdateBuffer& b = it->second; ui8& byte = b.bits[index >> DIV_8_SHIFT]; BlockIndex bitMask = (1 << (index & MOD_8_AND)); // Don't set it twice if (!(byte & bitMask)) { byte |= bitMask; b.toUpdate.push_back(index); } } } // You must manually call finish on all buffers then clear it yourself std::unordered_map buffers; }; #endif // VoxelUpdateBuffer_h__ ================================================ FILE: SoA/VoxelUpdateOrder.inl ================================================ #pragma once #include "Constants.h" // Randomly generated permutation of 32768 for fast "random" block updates const int RandomUpdateOrder[CHUNK_SIZE] = { 41, 18467, 6334, 26500, 19169, 15724, 11478, 29358, 26962, 24464, 5705, 28145, 23281, 16827, 9961, 491, 2995, 11942, 4827, 5436, 32391, 14604, 3902, 153, 292, 12382, 17421, 18716, 19718, 19895, 5447, 21726, 14771, 11538, 1869, 19912, 25667, 26299, 17035, 9894, 28703, 23811, 31322, 30333, 17673, 4664, 15141, 7711, 28253, 6868, 25547, 27644, 32662, 42, 20037, 12859, 8723, 9741, 27529, 778, 12316, 3035, 22190, 1842, 288, 30106, 9040, 8942, 19264, 22648, 27446, 23805, 15890, 6729, 24370, 15350, 15006, 31101, 24393, 3548, 19629, 12623, 24084, 19954, 18756, 11840, 4966, 7376, 13931, 26308, 16944, 32439, 24626, 11323, 5537, 21538, 16118, 2082, 22929, 16541, 4833, 31115, 4639, 29658, 22704, 9930, 13977, 2306, 31673, 22386, 5021, 28745, 26924, 19072, 6270, 5829, 26777, 15573, 5097, 16512, 23986, 13290, 9161, 18636, 22355, 24767, 23655, 15574, 4031, 12052, 27350, 1150, 16941, 21724, 13966, 3430, 31107, 30191, 18007, 11337, 15457, 12287, 27753, 10383, 14945, 8909, 32209, 9758, 24221, 18588, 6422, 24946, 27506, 13030, 16413, 29168, 900, 32591, 18762, 1655, 17410, 6359, 27624, 20537, 21548, 6483, 27595, 4041, 3602, 24350, 10291, 30836, 9374, 11020, 4596, 24021, 27348, 23199, 19668, 24484, 8281, 4734, 53, 1999, 26418, 27938, 6900, 3788, 18127, 467, 3728, 14893, 24648, 22483, 17807, 2421, 14310, 6617, 22813, 9514, 14309, 7616, 18935, 17451, 20600, 5249, 16519, 31556, 22798, 30303, 6224, 11008, 5844, 54, 14989, 149, 3195, 20485, 3093, 14343, 30523, 1587, 29314, 9503, 7448, 25200, 13458, 6618, 20580, 19796, 14798, 15281, 19589, 20798, 28009, 27157, 20472, 23622, 18538, 12292, 6038, 24179, 18190, 29657, 7958, 6191, 19815, 22888, 19156, 11511, 16202, 2634, 24272, 20055, 20328, 22646, 26362, 4886, 18875, 28433, 29869, 20142, 23844, 1416, 21881, 31998, 10322, 18651, 10021, 5699, 3557, 28476, 27892, 24389, 5075, 10712, 2600, 2510, 21003, 26869, 17861, 14688, 13401, 9789, 15255, 16423, 5002, 10585, 24182, 10285, 27088, 31426, 28617, 23757, 9832, 30932, 4169, 2154, 25721, 17189, 19976, 31329, 2368, 28692, 21425, 10555, 3434, 16549, 7441, 9512, 30145, 18060, 21718, 3753, 16139, 12423, 16279, 25996, 16687, 12529, 22549, 17437, 19866, 12949, 193, 23195, 3297, 20416, 28286, 16105, 24488, 16282, 12455, 25734, 18114, 11701, 31316, 20671, 5786, 12263, 4313, 24355, 31185, 20053, 912, 10808, 1832, 20945, 32427, 27756, 28321, 19558, 23646, 27982, 481, 4144, 23196, 20222, 7129, 2161, 5535, 20450, 11173, 10466, 12044, 21659, 26292, 26439, 17253, 20024, 26154, 29510, 4745, 20649, 13186, 8313, 4474, 28022, 2168, 14018, 18787, 9905, 17958, 7391, 10202, 3625, 26477, 4414, 9314, 25824, 29334, 25874, 24372, 20159, 11833, 28070, 7487, 28297, 7518, 8177, 17773, 32270, 1763, 2668, 17192, 13985, 3102, 8480, 29213, 7627, 4802, 4099, 30527, 2625, 1543, 1924, 11023, 29972, 13061, 14181, 31003, 27432, 17505, 27593, 22725, 13031, 8492, 142, 17222, 31286, 13064, 7900, 19187, 8360, 22413, 30974, 14270, 29170, 235, 30833, 19711, 25760, 18896, 4667, 7285, 12550, 140, 13694, 2695, 21624 , 28019, 2125, 26576, 21694, 22658, 26302, 17371, 22466, 4678, 22593, 23851, 25484, 1018, 28464, 21119, 23152, 2800, 18087, 31060, 1926, 9010, 4757, 32170, 20315, 9576, 30227, 12043, 22758, 7164, 5109, 7882, 17086, 29565, 3487, 29577, 14474, 32354, 25627, 5629, 31928, 25423, 28520, 6902, 14962, 123, 24596, 3737, 13261, 10195, 256, 1264, 8260, 6202, 8116, 5030, 20326, 29011, 30771, 6411, 32717, 21153, 21520, 29790, 14924, 30188, 21763, 4940, 20851, 18662, 13829, 30900, 17713, 18958, 17578, 8365, 13007, 11477, 1200, 26058, 6439, 2303, 12760, 19357, 2324, 6477, 5108, 21113, 14887, 19801, 22850, 14460, 22428, 12993, 27384, 19405, 6540, 31111, 28704, 12835, 137, 6072, 29350, 18823, 14485, 20556, 23216, 1626, 9357, 8526, 13357, 29337, 23271, 23869, 29361, 12896, 13022, 29617, 10112, 12717, 18696, 11585, 24041, 24423, 24129, 24229, 4565, 6559, 8932, 22296, 29855, 12053, 16962, 3584, 29734, 6654, 16972, 21457, 14369, 22532, 2963, 2607, 2483, 911, 11635, 10067, 22848, 4675, 12938, 2223, 22142, 23754, 6511, 22741, 20175, 21459, 17825, 3221, 17870, 32211, 31934, 15205, 31783, 23850, 17398, 22279, 22701, 12193, 12734, 1637, 26534, 5556, 1993, 10176, 25705, 6962, 10548, 15881, 300, 14413, 16641, 19855, 24855, 13142, 11462, 27611, 30877, 20424, 547, 1752, 18443, 28296, 12673, 10040, 9313, 875, 20072, 12818, 610, 1017, 14932, 28112, 30695, 13169, 23831, 20040, 26488, 28685, 19090, 19497, 2589, 25990, 15145, 19353, 19314, 32500, 26740, 22044, 11258, 335, 8759, 11192, 7605, 25264, 12181, 28503, 3829, 23775, 20608, 29292, 5997, 17549, 29556, 25561, 31627, 6467, 29541, 26129, 31240, 27813, 29174, 20601, 6077, 20215, 8683, 8213, 23992, 32378, 5601, 23392, 15759, 2670, 26428, 28027, 4084, 10075, 18786, 15498, 24970, 6287, 23847, 546, 503, 21221, 22663, 5706, 2363, 32297, 22171, 27489, 18240, 12164, 25542, 7619, 20913, 7591, 6704, 31818, 9232, 750, 25205, 4975, 1539, 303, 11422, 21098, 11247, 13584, 13648, 2971, 17864, 22913, 11075, 21545, 28712, 17546, 18678, 1769, 15262, 8519, 32362, 28289, 15944, 2865, 18540, 23245, 25508, 28318, 27870, 9601, 28323, 21132, 24472, 27152, 25087, 28570, 29763, 29901, 17103, 14423, 3527, 11600, 26969, 14015, 5565, 28, 21543, 25347, 2088, 2943, 12637, 22409, 26463, 5049, 4681, 1588, 11342, 608, 79, 32055, 1758, 29954, 20888, 14146, 690, 7949, 12843, 21430, 25620, 748, 27067, 4536, 20783, 18035, 261, 15185, 7038, 9853, 25629, 11224, 15748, 19923, 3359, 301, 24766, 4944, 14955, 23318, 775, 25411, 21025, 20355, 31001, 32447, 9496, 18584, 9515, 17964, 23342, 8075, 17913, 16142, 31196, 21948, 25072, 20426, 14606, 26173, 24429, 474, 6705, 20626, 29812, 19375, 30093, 16565, 16036, 14736, 29141, 30814, 5994, 8256, 6652, 23936, 30838, 20482, 1355, 21015, 1131, 18230, 17841, 14625, 2011, 731, 4186, 19690, 1650, 5662, 21634, 10893, 10353, 21416, 13452, 14008, 7262, 22233, 5454, 16303, 16634, 26303, 14256, 148, 11124, 12317, 4213, 27109, 24028, 29200, 21080, 21318, 16858, 24050, 24155, 31361, 15264, 11903, 3676, 29643, 26909, 14902, 3561, 28489, 24948, 1282, 13653, 30674, 2220, 5402, 6923, 3831, 19369, 3878, 20259, 19008, 22619, 23971, 30003, 21945, 9781, 26504, 12392, 837, 25313, 6698, 5589, 12722, 5938, 19037, 6410, 31461, 6234, 12508, 32753, 3959, 6493, 1515, 25269, 24937, 28869, 58, 14700, 13971, 26264, 15117, 16215, 24555, 7815, 18330, 3039, 30212, 29288, 28082, 1954, 16085, 20710, 32588, 24774, 8380, 29815, 25951, 6541, 18115, 1679, 17110, 25898, 23073, 788, 23977, 18132, 29956, 28689, 26113, 10008, 12941, 15790, 1723, 21363, 31993, 25184, 24778, 7200, 5071, 1885, 21974, 1071, 11333, 22867, 26153, 14295, 388, 20825, 9676, 15629, 28650, 2598, 3309, 4693, 4686, 30080, 10116, 12249, 26667, 1528, 26679, 7864, 29421, 8405, 8826, 6816, 7516, 27726, 28666, 29087, 27681, 19964, 1340, 5686, 6021, 11662, 14721, 6064, 29309, 20415, 17902, 29873, 17124, 23941, 1003, 21, 28423, 27531, 4806, 12268, 9318, 5602, 173, 24307, 23481, 1012, 21136, 26630, 24114, 26809, 32064, 23556, 12290, 21293, 29996, 29152, 1054, 25345, 14708, 248, 7491, 13712, 5131, 30114, 16439, 32523, 24722, 29704, 6995, 1052, 31832, 7479, 18238, 26423, 27918, 10866, 17659, 799, 28486, 21196, 7462, 26633, 12158, 12022, 1146, 18392, 13037, 3925, 10647, 29458, 6602, 30807, 4098, 27830, 32089, 14600, 7278, 20799, 18352, 20448, 13882, 540, 28315, 4575, 8762, 9567, 22336, 18397, 31418, 19897, 25828, 13851, 26816, 24230, 4449, 16925, 658, 229, 24520, 10940, 29560, 15147, 25162, 32608, 20675, 792, 22361, 105, 16398, 23146, 8714, 26946, 21188, 19569, 8638, 27663, 15075, 24515, 11521, 475, 15615, 20528, 13234, 12570, 905, 9464, 19557, 28962, 14161, 30524, 12549, 17469, 330, 32140, 28350, 14333, 22925, 10910, 19737, 16336, 18337, 1278, 12393, 7636, 30714, 28164, 31591, 19949, 19135, 2505, 13337, 10004, 16337, 2623, 28664, 9970, 25608, 30568, 19281, 7085, 9152, 18373, 28652, 8194, 9876, 23826, 28396, 7572, 6249, 14640, 32078, 18819, 943, 1028, 1941, 20289, 8419, 31994, 3805, 7585, 16216, 31450, 1615, 2609, 11064, 9166, 16893, 16074, 13509, 30300, 19695, 9573, 15589, 13161, 31172, 17968, 27358, 26031, 26268, 19426, 28510, 10422, 30774, 8779, 30910, 23552, 4182, 25391, 15495, 5764, 874, 1364, 31869, 28255, 4460, 31929, 6972, 26821, 26122, 32258, 21577, 32484, 25605, 30195, 27594, 7950, 16343, 754, 2481, 31730, 11672, 23439, 13428, 5912, 9762, 5967, 24408, 4415, 1908, 17223, 11759, 26434, 5204, 29486, 4319, 958, 5945, 26806, 8166, 18700, 11367, 17692, 21787, 28532, 30556, 32332, 3447, 32657, 18283, 26222, 29331, 2376, 28583, 26948, 12723, 17982, 22018, 14776, 9220, 32221, 696, 23856, 19490, 8925, 4324, 6486, 19677, 15969, 11643, 7534, 15677, 22668, 31068, 21991, 724, 7783, 16828, 7727, 29426, 15871, 10697, 17612, 18703, 11027, 11408, 5545, 9508, 7185, 30238, 24237, 26443, 21313, 22501, 8850, 25128, 2111, 23650, 28149, 32097, 1454, 15869, 681, 27465, 20267, 32246, 19793, 18634, 25472, 20972, 22830, 24901, 28442, 5177, 13877, 25770, 702, 14364, 1381, 16590, 8823, 2237, 8023, 755, 16595, 20169, 2327, 12042, 31310, 28182, 11058, 7926, 9487, 1670, 1115, 5651, 2258, 7213, 9860, 25783, 21286, 2742, 8610, 4472, 7128, 18434, 5841, 20718, 3503, 14867, 24865, 10938, 1881, 9257, 22750, 28614, 18598, 28458, 2661, 26063, 1369, 20807, 20278, 19489, 19435, 6365, 694, 7586, 1386, 7833, 32334, 13330, 26048, 8928, 29492, 12433, 23840, 6766, 1735, 19810, 11599, 11837, 21892, 618, 7328, 29352, 11369, 31244, 21794, 6608, 9252, 11647, 17432, 9535, 7208, 3264, 3497, 23243, 27649, 22015, 26841, 189, 16100, 19812, 30648, 9523, 19851, 24474, 28633, 29891, 27200, 19854, 9990, 25697, 4919, 17780, 22578, 12931, 1215, 3340, 13487, 899, 22525, 8483, 5538, 7492, 6193, 28252, 25011, 1560, 15834, 1840, 182, 2785, 18529, 228, 18805, 28791, 13392, 13210, 13549, 21578, 26979, 30971, 9277, 73, 20193, 1620, 21497, 13826, 31276, 19790, 6582, 13578, 11159, 20418, 26489, 159, 3449, 12924, 9072, 10380, 27008, 27967, 10208, 32233, 18503, 15370, 1328, 14196, 12074, 13722, 12611, 19019, 28761, 11056, 12890, 31163, 16683, 13716, 19932, 25452, 2741, 4954, 6813, 2862, 10396, 15460, 20615, 10904, 2599, 20136, 4680, 9198, 27032, 14387, 1333, 7240, 3517, 17006, 8670, 241, 18882, 25249, 3523, 516, 12105, 29621, 17095, 7296, 9916, 15678, 10178, 13579, 25058, 27577, 12750, 14007, 23729, 24081, 32751, 2678, 24676, 32625, 20899, 11784, 15565, 32549, 13608, 6172, 11243, 29929, 7514, 10168, 5055, 11191, 5973, 8922, 6748, 31411, 10986, 2144, 16446, 373, 26517, 14629, 29916, 5874, 15791, 15469, 22912, 8146, 30693, 9091, 9815, 26949, 26857, 20640, 26052, 236, 8551, 31414, 32767, 28162, 16955, 23183, 8394, 30180, 16097, 3065, 27065, 2513, 9261, 12578, 21078, 16878, 14140, 4611, 32708, 2445, 170, 29975, 13489, 24750, 6149, 3333, 13865, 22214, 17282, 27007, 32346, 8896, 16367, 28522, 4882, 31659, 17641, 7231, 2187, 32278, 6479, 6321, 6538, 207, 19447, 24208, 9646, 22276, 25759, 30189, 30422, 27666, 8486, 3455, 2028, 29614, 4860, 29253, 11777, 220, 12503, 10861, 22431, 29082, 32435, 14197, 22106, 8752, 15821, 17296, 26281, 26021, 24455, 15947, 27124, 18318, 9135, 11376, 1774, 29859, 24998, 31296, 9253, 6922, 10635, 1643, 28888, 8153, 13232, 4747, 28680, 19926, 25678, 6450, 14801, 24961, 14199, 20855, 26363, 5716, 10573, 31636, 32013, 6473, 28274, 1550, 24353, 1181, 4287, 2699, 18110, 18643, 17465, 7172, 2529, 9981, 2112, 13476, 4381, 8247, 26890, 16671, 8805, 1308, 30032, 3989, 9320, 23165, 15431, 9658, 11293, 17206, 26578, 16948, 2206, 27171, 18166, 3396, 16697, 31020, 23694, 15529, 14788, 30109, 17984, 11969, 28978, 21617, 4015, 16626, 3684, 9168, 17906, 25928, 12097, 28118, 24390, 15199, 11785, 14486, 19199, 12420, 31814, 18271, 15813, 27415, 6085, 318, 3580, 1331, 7267, 8387, 13444, 23186, 14507, 4360, 17827, 28074, 26431, 7152, 30271, 10268, 31772, 19885, 337, 309, 17604, 12677, 406, 7768, 29022, 19413, 5000, 542, 17537, 30038, 21388, 7355, 13289, 659, 3181, 13093, 16584, 10987, 10761, 20493, 8217, 9501, 17482, 29447, 15665, 10753, 22104, 15084, 19095, 13525, 30221, 3964, 21781, 4872, 8106, 3656, 3343, 32308, 27080, 16080, 14868, 21411, 13713, 20968, 3251, 27216, 12079, 28768, 17040, 579, 12933, 23779, 20663, 12259, 26653, 27936, 2095, 24365, 11874, 7720, 26835, 25680, 8976, 18455, 5725, 4071, 24808, 13559, 9156, 31734, 17832, 7905, 10440, 7375, 21562, 22885, 21962, 31880, 1836, 10797, 281, 10508, 10080, 5340, 12076, 9058, 578, 7740, 8546, 20474, 24773, 19097, 8880, 23335, 11072, 23400, 707, 22955, 20666, 4141, 23588, 12481, 17168, 32659, 19396, 16225, 1009, 22012, 18136, 11455, 32609, 25043, 742, 31740, 17922, 24512, 9248, 26018, 27368, 23717, 9714, 17650, 32646, 3335, 12759, 3169, 21895, 5303, 22640, 21979, 24199, 29105, 24791, 18661, 8681, 3652, 8753, 24033, 1166, 15987, 7042, 26253, 20083, 11420, 15814, 1862, 12244, 209, 7229, 20652, 18864, 4769, 30470, 15005, 21047, 1594, 21487, 24326, 3276, 21323, 32222, 7679, 23990, 1750, 24710, 29271, 17945, 29221, 28470, 20183, 23589, 23955, 4978, 24779, 5006, 13262, 20135, 23487, 27196, 29033, 31990, 12935, 19779, 15993, 14790, 24962, 18965, 11001, 19105, 11807, 24567, 2669, 3134, 1863, 1457, 12998, 3545, 13597, 14218, 8838, 14844, 7372, 8563, 21028, 29264, 28801, 14723, 13490, 7604, 809, 24227, 11197, 23692, 19771, 20363, 29301, 22363, 7721, 3565, 32741, 23445, 18610, 495, 16741, 15022, 1036, 29151, 23015, 8055, 3393, 8738, 15279, 19882, 1608, 12654, 3822, 1942, 24245, 1338, 144, 22290, 30951, 23154, 24604, 4623, 22225, 20078, 32634, 1228, 2330, 29733, 28223, 20594, 29130, 18846, 4987, 29445, 31667, 8616, 5750, 20489, 27338, 21963, 28135, 14697, 1473, 21630, 23224, 31517, 26737, 31339, 1190, 27372, 10293, 3855, 6734, 9561, 332, 27606, 8184, 7075, 28382, 14119, 6741, 30432, 24684, 12779, 12279, 31497, 20667, 125, 24125, 24118, 12737, 18028, 1413, 20577, 10737, 14091, 32213, 22795, 16060, 21901, 8793, 3432, 2136, 4580, 14875, 5907, 21184, 31009, 8719, 26790, 20476, 30041, 3351, 8329, 16290, 22974, 23072, 3591, 12189, 15787, 812, 3239, 32576, 3053, 17063, 10681, 25903, 27005, 24176, 18479, 1695, 6139, 1802, 13998, 21083, 23639, 29515, 27621, 29993, 15826, 15722, 23838, 24828, 12581, 24399, 8978, 11891, 1023, 26943, 24834, 14243, 7349, 2702, 8707, 20502, 25141, 10687, 8346, 15891, 24637, 18413, 11400, 22816, 1055, 13162, 8935, 29126, 19410, 19877, 11382, 26260, 27189, 26705, 13874, 2663, 20722, 1573, 22566, 16360, 32527, 32618, 7811, 28245, 9467, 1811, 26867, 13189, 10542, 13063, 21547, 30502, 32201, 9099, 25023, 17226, 600, 30048, 21051, 1570, 8636, 9458, 25967, 8456, 9405, 11531, 29962, 26819, 7975, 10556, 7531, 907, 8044, 5, 26803, 3388, 18915, 7450, 12319, 6272, 25791, 29383, 10133, 1775, 24642, 23569, 18300, 17954, 12078, 3585, 2257, 25333, 893, 10490, 10103, 4750, 17233, 10722, 24271, 19611, 18990, 30338, 21641, 23258, 19047, 2352, 31057, 479, 25302, 2133, 30558, 10002, 15568, 4422, 16895, 6135, 18008, 12361, 16742, 22194, 23699, 23188, 20178, 4042, 822, 31742, 1847, 7469, 16345, 4380, 1384, 14964, 25710, 20061, 25385, 20073, 27504, 9462, 2182, 28102, 11069, 5154, 3529, 30775, 1559, 19149, 3730, 2244, 10844, 13049, 14118, 24065, 31088, 19552, 28773, 18470, 29731, 6747, 7511, 5869, 14398, 10498, 7103, 27352, 25679, 28053, 13043, 14522, 597, 1563, 25834, 9850, 17022, 31249, 21911, 9492, 31207, 28580, 15477, 27616, 29876, 19178, 5220, 14615, 22348, 26798, 1706, 1163, 16857, 2883, 1662, 18902, 28262, 19420, 19770, 19022, 9273, 25841, 12686, 31100, 3917, 1259, 1892, 13698, 11267, 11749, 65, 10389, 6932, 25619, 16081, 2003, 30747, 13028, 18631, 14589, 32302, 18630, 19172, 19864, 6407, 12295, 25428, 29681, 18490, 26610, 26177, 639, 25236, 6459, 20643, 16840, 27633, 27037, 23893, 22630, 20274, 32225, 28782, 1783, 17461, 8290, 19662, 22307, 20997, 737, 18423, 8890, 16717, 6640, 32727, 5566, 4883, 23661, 22659, 18245, 20386, 2249, 2364, 19601, 30427, 17209, 1497, 27283, 29250, 18058, 8421, 175, 190, 26787, 3271, 31078, 27999, 12504, 24979, 2138, 10700, 530, 2461, 12118, 205, 7540, 18828, 24459, 11622, 30498, 3760, 27098, 30819, 10481, 245, 19567, 18493, 28596, 19161, 7746, 10538, 7670, 20538, 21476, 21555, 24917, 16371, 16760, 5752, 13758, 15433, 1903, 29065, 726, 2401, 3034, 30876, 10393, 203, 27273, 18792, 16193, 30950, 30137, 32000, 3690, 28211, 32320, 24668, 2293, 7498, 989, 15248, 3879, 31355, 11149, 604, 669, 550, 25598, 25449, 25436, 24599, 20123, 1443, 1731, 18154, 22861, 4434, 9385, 23967, 10816, 11393, 16704, 29866, 645, 1, 30749, 18684, 488, 22667, 10048, 32389, 13930, 512, 19814, 16090, 5427, 23743, 1604, 10599, 16474, 7195, 506, 5158, 17589, 9858, 27809, 17889, 11447, 40, 3818, 9364, 17975, 26, 25089, 2503, 30066, 26412, 6840, 31632, 14676, 25395, 28641, 29986, 17651, 21109, 8187, 30858, 21085, 23390, 988, 18686, 4755, 11381, 28128, 25502, 24277, 607, 26424, 30782, 3872, 832, 635, 14428, 6646, 20889, 6478, 10883, 24925, 21265, 32305, 5045, 20778, 5821, 13855, 2520, 12927, 31551, 25134, 27251, 22675, 13336, 16334, 21001, 2737, 9310, 5974, 7590, 20356, 31784, 1858, 10935, 1925, 31158, 1825, 18718, 13573, 4712, 31689, 4998, 32386, 7162, 1717, 8692, 19539, 28047, 10946, 19103, 27, 24115, 839, 25858, 20829, 2645, 3394, 17199, 19645, 14272, 675, 21862, 1880, 13773, 2480, 11238, 26897, 3542, 29608, 19203, 23277, 6125, 20134, 1401, 9078, 18382, 31421, 20736, 27478, 17939, 21138, 18721, 1254, 10663, 1677, 21575, 6724, 25981, 27700, 7961, 28862, 16002, 18448, 25095, 684, 24016, 15137, 9507, 13993, 21284, 2944, 106, 821, 7058, 24643, 17668, 10677, 119, 29857, 23041, 8891, 32095, 1623, 6915, 8072, 17929, 841, 4715, 17615, 12536, 14957, 27759, 700, 31896, 5093, 24241, 11829, 12448, 5227, 11798, 16224, 10324, 12274, 18133, 30925, 15038, 12170, 6862, 31175, 5084, 11909, 14878, 31860, 27085, 29400, 23024, 14193, 23105, 9412, 15765, 5767, 15407, 30147, 13784, 704, 30816, 9834, 10891, 621, 18085, 27734, 25190, 20542, 17998, 22086, 1929, 15621, 31002, 22597, 21376, 24254, 29669, 18108, 30235, 12493, 26068, 6366, 9102, 2438, 12600, 10819, 14318, 2290, 14984, 16339, 2556, 25808, 4632, 21478, 26814, 13787, 7239, 30690, 25020, 10827, 2554, 1988, 32459, 16798, 13642, 19002, 16321, 52, 13946, 7056, 18509, 29833, 17708, 19761, 6533, 20686, 14804, 26385, 10142, 18842, 17260, 22161, 1583, 4343, 9578, 1187, 113, 30609, 4591, 5934, 31746, 656, 29761, 25012, 28411, 11959, 16251, 18738, 13370, 26124, 5507, 8007, 17584, 10951, 23101, 14489, 24958, 15441, 1790, 17013, 403, 14855, 21060, 7093, 8472, 25402, 2673, 13543, 7373, 6266, 27651, 15275, 21528, 532, 30982, 5469, 32490, 32292, 17107, 32495, 2451, 17953, 28392, 22570, 13519, 19472, 23407, 22494, 9505, 28440, 20383, 14262, 21409, 28607, 18038, 31011, 5471, 11171, 20654, 29947, 11514, 14523, 21229, 489, 31369, 17887, 29756, 26632, 7470, 25739, 30902, 8522, 7283, 8160, 17553, 24705, 27091, 30457, 25386, 1737, 4629, 32714, 28317, 22045, 12356, 13388, 31265, 3154, 525, 32373, 27760, 26919, 25631, 6738, 21267, 25776, 32033, 24314, 16320, 13053, 24007, 16469, 24216, 9722, 19842, 29007, 15463, 30627, 1026, 13793, 28630, 18717, 23043, 1460, 25314, 31037, 17117, 1334, 20620, 14171, 26792, 8964, 19154, 18866, 14693, 760, 32091, 13000, 12212, 21100, 7551, 25476, 6379, 10943, 17877, 3789, 361, 11385, 8272, 11434, 15144, 29561, 25563, 14504, 12946, 23888, 20308, 12157, 1430, 5123, 6464, 4074, 4346, 13837, 1981, 25318, 26611, 16292, 17591, 3832, 27123, 6461, 16991, 1595, 27330, 28498, 17369, 17291, 8400, 14179, 24117, 2317, 19914, 29865, 1441, 5936, 21867, 7028, 1602, 27909, 13973, 17981, 11503, 26569, 31760, 1883, 25367, 5385, 28402, 5230, 17157, 28681, 15567, 8310, 1866, 3687, 13171, 3477, 1414, 2934, 6238, 27671, 2047, 26115, 4592, 27311, 2834, 1405, 32585, 7171, 32539, 22740, 22530, 13675, 24320, 25790, 13377, 10998, 16586, 21604, 4489, 19631, 29744, 8388, 30433, 30216, 29937, 18259, 5927, 14609, 28119, 21479, 22716, 6300, 21396, 16853, 11458, 20830, 24593, 1016, 60, 14854, 32515, 26160, 12294, 12054, 26333, 146, 4229, 10471, 10428, 10559, 22241, 31763, 22688, 16366, 12033, 26685, 2270, 25329, 2492, 22860, 2208, 8982, 22162, 31405, 6878, 11979, 1897, 7887, 31144, 14469, 29920, 17540, 30700, 7266, 18633, 31567, 24557, 32457, 26134, 2305, 18135, 12258, 2593, 17843, 21693, 16466, 22951, 18140, 11094, 12937, 5174, 2512, 6097, 9551, 32319, 21817, 23492, 21292, 20237, 27530, 613, 25296, 21710, 2665, 5628, 5809, 28786, 2024, 18840, 8182, 28569, 6120, 27393, 6292, 32274, 14214, 12527, 26807, 13194, 8705, 14713, 7011, 18913, 6693, 19664, 26203, 28995, 17762, 14381, 31644, 212, 1048, 21768, 26624, 22511, 7954, 4235, 1127, 14633, 28240, 30855, 15127, 31231, 23817, 2340, 30040, 15100, 28400, 6558, 30359, 29941, 30220, 16430, 5831, 21295, 3537, 15623, 30886, 2927, 28029, 544, 16211, 11754, 26880, 12253, 3293, 21321, 20785, 26528, 10443, 1343, 32462, 24671, 9270, 7452, 9375, 14631, 13640, 8934, 18040, 7986, 28834, 14248, 32036, 28765, 12796, 10312, 31894, 25052, 1093, 23987, 32758, 2922, 29644, 32086, 20170, 5450, 27182, 7301, 13293, 24099, 23332, 19044, 2707, 75, 1382, 19050, 15026, 14066, 11738, 32229, 19623, 13843, 441, 3380, 31565, 7918, 3659, 1363, 12628, 6329, 1088, 28854, 8613, 30384, 18569, 666, 30839, 29684, 27837, 3402, 23115, 23292, 31793, 2872, 29448, 732, 11153, 15948, 24395, 25197, 4508, 22908, 17411, 30155, 7665, 26781, 3202, 17610, 30655, 27256, 27705, 8965, 18023, 20769, 11715, 20253, 2235, 23684, 29478, 2578, 21840, 833, 19285, 26900, 10968, 23994, 16633, 13885, 348, 18281, 18032, 16256, 356, 10872, 29765, 23901, 12306, 31102, 17860, 2924, 29527, 7137, 8343, 9768, 28233, 3995, 29171, 7603, 28942, 18011, 7879, 1499, 14512, 32255, 30381, 32084, 23234, 10258, 21926, 5906, 5191, 14443, 6188, 30099, 3197, 26484, 5224, 19093, 917, 13224, 28094, 3582, 8867, 8627, 22005, 11146, 3176, 16589, 20540, 18237, 32228, 18759, 30034, 28956, 3175, 2403, 28042, 29795, 15811, 5225, 21143, 21595, 21016, 26615, 28804, 21006, 28368, 2152, 847, 698, 1325, 28313, 28023, 22312, 24618, 18457, 18396, 8339, 21086, 2617, 7846, 27043, 2048, 12911, 16280, 23455, 26358, 26847, 28870, 990, 3873, 4327, 3413, 3454, 2434, 12472, 24450, 17241, 4641, 5646, 23525, 28213, 16824, 30138, 8695, 25403, 12594, 7993, 26067, 22187, 4640, 25079, 10041, 18125, 12534, 31130, 23000, 4907, 3183, 27692, 25293, 5117, 7717, 20403, 3943, 17893, 10639, 32261, 26925, 31882, 19978, 10748, 2850, 22892, 26444, 10654, 23623, 31080, 12036, 2130, 21346, 826, 16061, 3904, 10238, 17571, 26886, 16405, 5299, 22056, 11728, 1267, 3751, 2019, 19991, 31050, 5717, 25846, 17093, 3187, 1421, 31936, 6867, 28299, 3052, 30289, 1201, 3328, 24942, 7104, 1768, 383, 26641, 16296, 11262, 10720, 21244, 31701, 9625, 21219, 30016, 18174, 10984, 10459, 13149, 17014, 5695, 31126, 5032, 31664, 1537, 30064, 8363, 4651, 1219, 24290, 25917, 21683, 2560, 26584, 27252, 3586, 1915, 26248, 12137, 2397, 424, 23168, 20027, 28148, 2647, 10382, 30835, 27669, 30566, 20877, 1014, 19104, 484, 31989, 11495, 26811, 1293, 28892, 6798, 1990, 11299, 14890, 8493, 29074, 1549, 13774, 32379, 20173, 24069, 29218, 26865, 16833, 15238, 19911, 13252, 6463, 6508, 25149, 23088, 12245, 6231, 2611, 10106, 19271, 12368, 6421, 15258, 4157, 9647, 4982, 31703, 24122, 17723, 906, 18706, 10189, 11156, 31706, 12351, 30111, 7118, 2261, 2097, 11836, 10591, 13406, 4510, 25813, 14651, 25880, 27844, 24743, 7741, 11245, 14578, 4949, 21705, 24514, 19785, 9227, 8624, 17204, 28356, 23917, 6227, 555, 10102, 14286, 19468, 11698, 8979, 30959, 12489, 10758, 30793, 1520, 28410, 17132, 27089, 27296, 11950, 23112, 20122, 32448, 7672, 25836, 18277, 5553, 13976, 10670, 9085, 29463, 1751, 1160, 19751, 29001, 435, 16693, 15072, 17916, 6716, 25328, 1511, 2898, 11838, 30394, 18939, 11544, 1056, 12826, 12902, 26758, 24588, 12021, 22586, 14108, 563, 29607, 30871, 26947, 31625, 1280, 13245, 31451, 14062, 10426, 4719, 30101, 29091, 4047, 13785, 23231, 7406, 8684, 21954, 16769, 14709, 27956, 22421, 15945, 13465, 16016, 21860, 16497, 18335, 5190, 5677, 28049, 21005, 7886, 23388, 18675, 13143, 17377, 10130, 28487, 26555, 22482, 25253, 15000, 27754, 289, 3320, 25215, 12008, 2145, 940, 27799, 2997, 9707, 8454, 304, 8237, 10485, 12499, 32149, 459, 3459, 18682, 12983, 15351, 16129, 17137, 5033, 16600, 2719, 23683, 11857, 8968, 1710, 21968, 2569, 2641, 27732, 12437, 11684, 3365, 3568, 30413, 31493, 126, 19692, 7640, 12087, 15012, 13810, 5820, 14835, 1663, 10615, 25897, 18677, 2774, 10134, 27553, 19533, 28878, 2286, 25624, 2734, 18075, 15349, 30293, 26984, 1518, 27792, 6504, 28822, 4055, 31708, 462, 24473, 20084, 24160, 2026, 26053, 19397, 11822, 19750, 21337, 13917, 4006, 4931, 22197, 27505, 151, 10357, 6599, 28290, 1898, 985, 21447, 1084, 592, 18725, 8996, 781, 32283, 15338, 17342, 30248, 30349, 1674, 970, 5506, 6767, 5053, 9793, 28505, 24146, 3609, 13924, 31335, 408, 12239, 26206, 24209, 15492, 16499, 18175, 1986, 23061, 13647, 15618, 25013, 26727, 15712, 27896, 21909, 27841, 20130, 9090, 9034, 7915, 6286, 31242, 29958, 1344, 3704, 22037, 5416, 5893, 1949, 1944, 30510, 22777, 29563, 26200, 14590, 32478, 28325, 17118, 25668, 10006, 9729, 24008, 14541, 225, 15536, 11980, 716, 1461, 15384, 28747, 32513, 30945, 6204, 30382, 7523, 26364, 1411, 16040, 250, 11504, 23483, 21766, 16141, 32067, 10845, 10506, 9088, 23256, 25107, 8266, 5857, 22678, 31090, 12674, 14155, 25191, 8990, 853, 32630, 10996, 8917, 29081, 3967, 1707, 8234, 28246, 32134, 19402, 8239, 3426, 30972, 31581, 8314, 10350, 4586, 5279, 17776, 25822, 6879, 3344, 15812, 5676, 3164, 7357, 3618, 30618, 24312, 10144, 16561, 28180, 18200, 1698, 21856, 18061, 29345, 5939, 19808, 9290, 17452, 979, 3682, 21419, 29928, 22284, 24547, 1046, 15675, 18613, 18709, 13371, 28642, 30069, 6336, 3698, 21501, 26034, 18129, 21358, 1436, 28469, 22897, 28504, 4798, 4202, 32702, 1238, 15032, 20065, 12516, 14481, 15579, 9023, 1727, 1771, 2411, 29344, 6140, 24816, 28488, 15319, 31844, 5513, 29801, 32250, 3379, 23283, 4364, 27038, 3012, 3184, 21759, 23501, 9574, 28733, 5059, 27714, 31348, 6595, 31153, 24541, 32440, 15377, 31681, 565, 19891, 138, 13310, 20967, 15055, 30063, 27224, 18222, 31711, 27441, 6648, 26804, 27834, 19368, 2583, 8872, 3812, 19974, 19176, 5137, 20633, 3241, 5745, 18018, 2167, 30682, 19252, 846, 8956, 25524, 6966, 15581, 8144, 3401, 496, 15296, 11876, 25027, 1205, 2018, 4291, 2810, 29189, 16191, 22471, 6259, 23700, 20217, 2016, 1943, 14624, 17064, 2685, 1157, 2684, 7666, 9275, 3231, 17632, 976, 6046, 29164, 6003, 13152, 31219, 8326, 347, 30691, 19649, 1547, 10405, 24346, 1025, 171, 14336, 4795, 29062, 32674, 5532, 63, 15896, 3210, 28959, 5692, 14550, 15290, 13795, 13852, 17350, 8315, 10036, 580, 30372, 9984, 8729, 2534, 6050, 31817, 16806, 1936, 25584, 10790, 31039, 413, 19066, 23116, 6388, 9425, 13101, 5610, 31093, 19982, 28621, 5193, 30459, 16754, 4593, 14895, 20280, 5638, 25167, 17420, 4345, 16883, 23865, 24696, 2087, 10390, 22632, 20189, 17839, 30831, 13817, 22129, 6753, 10679, 28928, 10600, 8252, 32106, 19861, 2151, 32011, 234, 7813, 14907, 828, 2751, 617, 11211, 25429, 2264, 15966, 19531, 1224, 10698, 19522, 2440, 27587, 15257, 23158, 4464, 13762, 8510, 2238, 2358, 31811, 13165, 5946, 13540, 27297, 8042, 14464, 23194, 12584, 10270, 4019, 3505, 1206, 29238, 2610, 28746, 8833, 7611, 19067, 415, 6645, 1490, 6354, 23448, 24919, 13079, 27988, 26321, 7592, 2920, 29086, 29751, 8652, 71, 31151, 14122, 26859, 7997, 10999, 1899, 4120, 1320, 3372, 16984, 4912, 29353, 23953, 19485, 20741, 10199, 11567, 4208, 6844, 1516, 1029, 4879, 21108, 17207, 5259, 25706, 23849, 24075, 28026, 20124, 27607, 17774, 2884, 20128, 24546, 3145, 20481, 11714, 20149, 24168, 13338, 20307, 26793, 23504, 1105, 1697, 18217, 15955, 32138, 2343, 26133, 3476, 679, 30176, 19555, 25633, 31816, 930, 10252, 19904, 20279, 12488, 24870, 28636, 26072, 3569, 26701, 15415, 18086, 4231, 17480, 19975, 1660, 20514, 8938, 16657, 14990, 19438, 387, 15447, 5242, 29481, 16070, 29936, 24061, 27219, 195, 20990, 12833, 8672, 30489, 21302, 502, 26928, 2143, 24898, 2871, 10945, 28406, 2222, 8686, 5794, 23577, 12844, 18683, 30608, 13240, 485, 16464, 3160, 20337, 16653, 27168, 28130, 32483, 1466, 4858, 22512, 1102, 3318, 13201, 32340, 21276, 2957, 4154, 10684, 15023, 1725, 26312, 8250, 13475, 20311, 26850, 19195, 5488, 11520, 26101, 28740, 24798, 29989, 18848, 6784, 1715, 12187, 14462, 28343, 18258, 16859, 8871, 14016, 8172, 22832, 24441, 17683, 16088, 31347, 29241, 18516, 18298, 39, 2771, 3205, 19997, 637, 5769, 23675, 29517, 9387, 15020, 6030, 17442, 21735, 3889, 20373, 3480, 12073, 10777, 786, 8381, 3292, 522, 18345, 20979, 31815, 15713, 2448, 19735, 2398, 2186, 16096, 20648, 2085, 14752, 521, 9260, 21982, 6636, 7688, 19919, 17236, 29908, 31176, 19278, 4198, 3028, 26386, 21011, 10976, 7547, 12121, 23202, 7245, 6702, 31648, 3081, 2572, 31598, 2370, 21920, 3201, 19359, 11080, 7298, 23391, 23960, 2063, 1145, 25867, 22033, 1675, 12136, 2058, 17775, 29813, 14376, 2819, 20272, 20703, 5086, 28868, 31942, 23542, 23495, 3566, 18456, 14186, 8437, 19198, 12626, 18364, 1085, 3244, 18908, 20340, 1360, 2814, 21154, 18012, 19752, 3338, 8706, 12525, 31862, 840, 23179, 3079, 32392, 433, 4113, 30392, 14145, 5991, 1969, 21839, 24308, 31923, 23762, 19473, 23681, 21970, 23147, 27053, 22708, 29313, 16715, 4429, 2680, 27689, 14663, 11771, 5541, 16259, 30444, 23572, 2514, 10966, 25988, 18170, 28168, 22199, 655, 3850, 31014, 12936, 16278, 19406, 1061, 10313, 29234, 4008, 23327, 17073, 23461, 2173, 9934, 2745, 25587, 3062, 3109, 6153, 26126, 20595, 1495, 864, 2406, 27370, 23859, 23305, 22496, 24833, 24223, 4852, 17879, 2180, 6126, 7793, 3249, 21465, 3037, 15444, 29990, 17271, 24087, 28099, 32561, 8596, 5715, 17552, 22492, 14753, 8257, 27590, 24959, 32208, 10886, 7809, 5865, 25686, 3705, 259, 22313, 2435, 31582, 4101, 10468, 22869, 456, 6731, 7578, 26074, 11395, 2542, 2974, 13316, 15024, 2214, 18585, 5722, 8971, 10057, 14095, 8174, 5038, 2183, 26658, 17295, 25722, 16983, 31747, 1220, 2232, 24300, 15590, 21480, 17408, 15505, 4426, 22858, 9818, 10557, 15561, 24120, 5028, 23656, 25713, 3332, 23092, 17182, 26467, 11320, 7358, 21074, 3987, 9094, 4983, 21364, 27264, 19714, 26746, 31030, 10425, 26255, 13163, 3218, 5792, 14231, 31854, 2880, 4718, 16128, 19948, 4720, 9079, 3852, 3766, 28713, 5808, 12126, 14825, 2926, 17518, 3220, 10035, 3085, 25456, 24147, 31391, 1909, 3360, 751, 16208, 3257, 22294, 12589, 17524, 7976, 9694, 4090, 28742, 1159, 7419, 2090, 21702, 24844, 20523, 26539, 20609, 11827, 6637, 8186, 27416, 1827, 15169, 17623, 14899, 22514, 10415, 3644, 32748, 23669, 10378, 3485, 17858, 7952, 9846, 27137, 5915, 22988, 24616, 7288, 2382, 30061, 20333, 3915, 31271, 25656, 1886, 9616, 24524, 12129, 27442, 1984, 30967, 7049, 23396, 18026, 8582, 32383, 18775, 29029, 30276, 14552, 29504, 12755, 11417, 22844, 3554, 19443, 6824, 3846, 270, 121, 15733, 31351, 19917, 15793, 21873, 12060, 3031, 9074, 5985, 29973, 3482, 13208, 27395, 11026, 26720, 26291, 3854, 10929, 32338, 2846, 10193, 8, 12254, 922, 2750, 30157, 2692, 13803, 21090, 7912, 12289, 32399, 1861, 7299, 12322, 22902, 27602, 6369, 28300, 12729, 9734, 12438, 30121, 5226, 16463, 29, 22972, 24348, 16453, 32544, 2307, 25518, 18333, 27437, 20695, 13179, 661, 23144, 31699, 10961, 21907, 6576, 13472, 20645, 3592, 28103, 628, 24196, 6811, 15535, 15835, 1229, 3716, 23532, 24956, 16158, 31434, 24620, 23296, 19724, 13120, 24526, 16406, 8983, 3445, 2509, 21691, 26588, 2476, 20664, 18104, 29340, 20353, 2942, 17004, 2568, 14852, 15625, 17611, 27842, 8724, 3100, 30824, 13590, 10146, 31362, 23855, 27804, 3699, 16909, 19381, 31611, 1064, 10083, 692, 10532, 17312, 26099, 6161, 11047, 22405, 29720, 17569, 1932, 4602, 22139, 21228, 27580, 31661, 21778, 27440, 28427, 22148, 1427, 24745, 16125, 7210, 4698, 25616, 30875, 24815, 31748, 16226, 1465, 27479, 19593, 4478, 3005, 18536, 15333, 11105, 28236, 29110, 15638, 28010, 28037, 14518, 8399, 29367, 17508, 13896, 13438, 23978, 22638, 8245, 4059, 24169, 744, 27362, 24769, 11049, 26518, 16467, 10558, 4167, 29048, 25603, 9190, 22186, 1226, 3603, 28055, 21300, 3801, 423, 3382, 5396, 4566, 25230, 21665, 26152, 32600, 3617, 1216, 2830, 10668, 3643, 3626, 26224, 17728, 29921, 31693, 23425, 23267, 19312, 734, 452, 5169, 31299, 30599, 8211, 1296, 6890, 1711, 2064, 30563, 30592, 7545, 14767, 21754, 2857, 30980, 24565, 2015, 30820, 10273, 2815, 27579, 2915, 9388, 19269, 11886, 13830, 10809, 30744, 7321, 18319, 1968, 3714, 13020, 28999, 20454, 25893, 21984, 2357, 22999, 2432, 18829, 5950, 32181, 4505, 10365, 2334, 29488, 19094, 697, 3376, 2226, 23017, 1422, 15253, 16117, 4977, 19160, 21505, 32744, 24591, 6562, 3576, 4854, 32122, 11134, 23016, 25050, 9193, 31851, 1852, 20747, 4649, 8061, 6344, 31856, 498, 2885, 14439, 2718, 9107, 5268, 28155, 4189, 7432, 13799, 4162, 16735, 3242, 2792, 3916, 26182, 19029, 25316, 3059, 30353, 4583, 22399, 1135, 8357, 20070, 15271, 22342, 31331, 31432, 2567, 457, 17686, 29500, 28882, 15718, 23035, 32323, 30490, 16678, 25960, 101, 20949, 30400, 30686, 27626, 29838, 29991, 4841, 18938, 11593, 17096, 3216, 4493, 1953, 59, 2122, 19109, 2285, 20381, 21561, 29959, 5584, 25923, 31380, 16731, 11849, 25442, 8606, 6850, 32558, 26040, 22769, 9223, 31983, 27977, 12083, 3400, 11158, 15606, 16938, 19463, 20920, 18366, 15758, 19685, 28904, 20229, 19298, 1576, 5470, 23175, 28774, 1683, 9988, 32045, 2946, 4418, 8275, 2469, 25519, 14341, 26197, 8545, 1654, 7158, 14058, 32003, 23791, 13341, 24837, 1142, 31098, 12675, 19736, 17438, 9958, 2828, 30110, 15013, 5284, 5253, 20727, 2539, 2426, 19670, 14536, 3281, 23370, 6549, 1348, 9077, 26199, 16835, 3573, 30008, 8351, 999, 27617, 5233, 2842, 27376, 25529, 17731, 11044, 20342, 27793, 2278, 7528, 3196, 12634, 4999, 17757, 1524, 581, 13623, 15941, 7131, 15749, 32092, 9299, 25575, 32356, 14363, 29207, 14726, 201, 6424, 24083, 7389, 20735, 22024, 18895, 1049, 6170, 11483, 1024, 30131, 28365, 11324, 19773, 30051, 18255, 4468, 22070, 16843, 10030, 15756, 10765, 3114, 2980, 1458, 12358, 8124, 6008, 25311, 6667, 21717, 15928, 17590, 32632, 30694, 13870, 2990, 25412, 14000, 18064, 29726, 29097, 19682, 17911, 20106, 9868, 8644, 31590, 16436, 17366, 11592, 24988, 18838, 15865, 27212, 23947, 7446, 19659, 28873, 5771, 21256, 11110, 4417, 10666, 26250, 12670, 13145, 31688, 3514, 31199, 25774, 7043, 9429, 5536, 6763, 20016, 21858, 23934, 29628, 2061, 29146, 3815, 21174, 32178, 10139, 12082, 14024, 20900, 1575, 19337, 15376, 11300, 19424, 15864, 32604, 29732, 18525, 18629, 22851, 2185, 3192, 4143, 21706, 32063, 16071, 6490, 4288, 14819, 19979, 27195, 30288, 16807, 845, 15374, 31223, 21156, 14820, 2847, 9766, 27222, 22957, 553, 26271, 6955, 9389, 48, 8029, 3128, 2039, 1527, 20394, 8490, 2078, 28564, 5517, 24192, 4964, 12618, 3148, 24056, 22990, 2282, 28288, 32400, 21301, 21493, 1927, 269, 2839, 21033, 8431, 28085, 27845, 19026, 5842, 19080, 18616, 4961, 1173, 15907, 5920, 27704, 22166, 2582, 244, 2782, 27006, 6491, 4336, 30475, 22195, 29177, 11763, 32735, 23592, 14935, 19217, 27664, 5881, 13467, 26721, 16952, 17322, 1828, 31909, 429, 21424, 17699, 14153, 22151, 4282, 5041, 31874, 8383, 8720, 2637, 2882, 14077, 18213, 5202, 8658, 28912, 23753, 22249, 5058, 16575, 8090, 24638, 6823, 32745, 24701, 17205, 11558, 3123, 24287, 1041, 14026, 2530, 6209, 27585, 935, 27630, 12531, 25674, 27492, 8100, 23071, 28163, 15009, 6430, 22794, 1151, 11736, 27937, 5015, 2452, 26754, 9782, 17933, 25188, 17855, 11960, 30834, 9923, 3084, 14659, 28143, 5943, 15347, 31943, 21389, 4217, 21788, 3198, 12280, 32, 5261, 1813, 6860, 28592, 2404, 7271, 4742, 12344, 32135, 16448, 25636, 12353, 7549, 13888, 2823, 11825, 9251, 6190, 7639, 4619, 3362, 5775, 17624, 14078, 4985, 30564, 29580, 377, 26350, 6048, 29631, 18494, 20216, 28110, 2314, 16038, 31606, 1263, 31677, 15862, 29304, 262, 11128, 28334, 14329, 28312, 24082, 5389, 24986, 28934, 12735, 2140, 18666, 13632, 13485, 15822, 20003, 5161, 11896, 26141, 2150, 1047, 5872, 30828, 4770, 4932, 27165, 25645, 8120, 1810, 4752, 25970, 6621, 3596, 15004, 8514, 31678, 23210, 31705, 30505, 4869, 471, 16344, 11830, 6382, 27261, 26395, 27151, 30821, 83, 1354, 29320, 30799, 2295, 31034, 30623, 30130, 20284, 2557, 25812, 30193, 6341, 6575, 30760, 17525, 6611, 22167, 26982, 5783, 23387, 30806, 715, 19679, 18268, 3942, 3553, 29407, 20335, 12460, 24105, 21511, 24992, 500, 227, 7016, 8060, 29616, 3440, 1991, 2923, 67, 7300, 27225, 31917, 19140, 30301, 28749, 1807, 14449, 8491, 18760, 24382, 4836, 2916, 2968, 27333, 28544, 5519, 4809, 16701, 6631, 8324, 25807, 12725, 22301, 2992, 3969, 28610, 21164, 13963, 24124, 31476, 486, 2851, 13997, 23724, 2231, 20067, 2987, 20840, 28891, 24955, 16028, 1661, 27903, 18227, 2890, 32453, 5398, 22237, 4782, 24848, 24404, 7048, 25565, 960, 22133, 13175, 6241, 5257, 12147, 17362, 23954, 2423, 29462, 21658, 1721, 20784, 10688, 32404, 16116, 3611, 2936, 26429, 24537, 13629, 18389, 9843, 7004, 20101, 2148, 12920, 4753, 5136, 1820, 7582, 31766, 30077, 1472, 1966, 26958, 4179, 1759, 5987, 10823, 397, 11096, 16594, 8901, 17653, 14520, 3288, 28087, 20, 28643, 4953, 10824, 30660, 2849, 30976, 2732, 27447, 28751, 16966, 25690, 868, 2726, 22828, 29797, 14144, 2507, 9449, 18833, 12003, 22910, 3240, 21027, 15610, 527, 15282, 26393, 17187, 7863, 23961, 2565, 1418, 19251, 139, 12081, 3558, 927, 6358, 29317, 7178, 17464, 17457, 6572, 7221, 16922, 25756, 10867, 21260, 9298, 25343, 5234, 31346, 24019, 25352, 29205, 8411, 16811, 21793, 17405, 6881, 3651, 32326, 17898, 21271, 15842, 5694, 31712, 21535, 16500, 10955, 4013, 20323, 650, 6688, 28940, 4010, 26017, 32118, 14948, 4077, 3495, 26419, 14648, 27860, 10171, 15851, 3695, 11223, 3637, 16649, 15800, 24407, 29339, 3765, 17177, 14812, 20641, 25304, 4496, 23162, 12537, 18551, 25851, 27205, 4457, 27819, 339, 29483, 31592, 14526, 2178, 15745, 6928, 4205, 15149, 32024, 5312, 9401, 25199, 24727, 5600, 22353, 705, 26542, 22567, 62, 12376, 14255, 26742, 16748, 3021, 22815, 14047, 11073, 1887, 4749, 32218, 5094, 14331, 7970, 29094, 8768, 982, 13178, 29293, 24981, 12695, 17586, 13929, 31905, 4278, 17859, 31119, 5089, 4900, 20238, 7054, 29061, 28654, 28176, 24171, 18147, 8503, 19511, 5126, 1772, 26107, 12205, 22599, 31802, 32366, 2004, 24435, 31321, 5732, 31820, 32324, 27733, 18367, 18899, 21054, 10167, 19212, 26114, 3101, 5793, 11977, 2829, 29125, 6831, 14889, 23326, 5627, 29329, 2489, 2657, 29244, 6612, 24387, 11957, 15503, 19491, 10482, 27024, 19, 13361, 952, 8496, 29456, 15183, 15597, 11554, 25032, 18102, 31509, 30687, 30752, 9081, 31738, 4630, 640, 1352, 22602, 15369, 3282, 19989, 14437, 21599, 35, 3179, 19654, 24652, 23399, 1371, 29676, 27912, 5293, 21871, 2649, 22590, 28281, 5206, 32144, 5319, 27934, 31868, 20228, 28294, 2704, 8785, 23774, 19184, 6651, 18884, 31980, 10915, 15189, 11681, 19074, 13759, 5850, 4546, 2310, 6427, 31893, 9421, 3161, 873, 9546, 21350, 21263, 16461, 21846, 23458, 9594, 434, 27459, 13502, 8246, 20420, 25254, 7953, 15760, 29354, 25419, 1808, 16616, 20113, 13335, 1669, 2096, 9265, 9370, 31669, 26525, 12746, 17644, 19512, 28054, 27923, 5105, 25726, 23201, 773, 25306, 3080, 4687, 31914, 30030, 24214, 3223, 28985, 2700, 18849, 2322, 13285, 6762, 30645, 1268, 21374, 5152, 23848, 2345, 1749, 22762, 1350, 30579, 1792, 12200, 26343, 17519, 7481, 3370, 8991, 2267, 19984, 12714, 210, 31555, 30653, 32436, 14386, 249, 2650, 32151, 28552, 25516, 1743, 17888, 9499, 12425, 26983, 909, 25331, 9814, 11502, 5970, 19537, 17598, 4224, 27267, 6851, 11888, 14463, 2205, 28694, 15741, 29934, 30416, 2812, 14081, 9695, 19618, 33, 25094, 27743, 2538, 7121, 1445, 29967, 8243, 12365, 28243, 17135, 16937, 4599, 4673, 13192, 30512, 19124, 31, 1245, 27242, 31843, 2738, 13818, 21875, 1692, 13910, 18361, 13132, 19728, 3712, 30327, 469, 5019, 28111, 31614, 17777, 22338, 15175, 18944, 30182, 27178, 19821, 6340, 3640, 7966, 2287, 8148, 17547, 9951, 12907, 19551, 3110, 6557, 291, 13009, 27034, 21576, 10540, 26023, 22574, 15454, 369, 11906, 5709, 24999, 1274, 23930, 573, 28287, 25816, 8884, 29863, 20031, 22604, 9783, 31198, 25422, 8010, 20987, 5604, 568, 31918, 30759, 24023, 25709, 4903, 30901, 2533, 8477, 14023, 523, 8828, 11435, 31306, 12517, 9009, 1000, 4125, 1265, 21590, 12891, 3631, 1538, 30792, 13776, 5478, 10991, 12693, 23409, 11620, 1468, 22662, 29206, 31344, 6034, 10842, 18440, 10089, 2773, 22642, 4901, 21486, 6146, 1248, 11486, 1271, 4230, 9466, 21474, 28065, 11788, 17349, 15827, 4248, 4799, 27741, 14246, 32474, 584, 16114, 31628, 32276, 23673, 14943, 12312, 3232, 14883, 13239, 30997, 5899, 28518, 27483, 21987, 18795, 27540, 1912, 1956, 4253, 30625, 1479, 13207, 4552, 3954, 30399, 13940, 2043, 32739, 23412, 14377, 26623, 11472, 11983, 3885, 3020, 18720, 16257, 14702, 27865, 10606, 12276, 25924, 10652, 30213, 19035, 1699, 28298, 1890, 13367, 19606, 11221, 30279, 28169, 27854, 27927, 31536, 24258, 27486, 21531, 31284, 5142, 6066, 26162, 15689, 15923, 29690, 25342, 9282, 31277, 12180, 19969, 32654, 18715, 16051, 24736, 11755, 15174, 18477, 1154, 24729, 10882, 27126, 19769, 11237, 1685, 15676, 13604, 460, 24161, 21197, 8595, 32316, 5485, 15062, 31602, 955, 7718, 2470, 8079, 8709, 9662, 10224, 11574, 7682, 7475, 28409, 2749, 31040, 29163, 14568, 6094, 4361, 5472, 25286, 28986, 6089, 5567, 4604, 30562, 25594, 9076, 598, 18604, 23533, 468, 4609, 4843, 2964, 3044, 11014, 29618, 14996, 16873, 7148, 29992, 31260, 27712, 30578, 16866, 25589, 4020, 23355, 29588, 1694, 5081, 31087, 5271, 9228, 4621, 13454, 10309, 27472, 4690, 13991, 13739, 24853, 31676, 12567, 17054, 3336, 22490, 4658, 19056, 10773, 4053, 327, 1002, 2896, 12888, 10539, 17585, 21841, 27975, 5520, 11560, 30846, 14581, 3859, 26650, 11989, 264, 13403, 21960, 21462, 8732, 20392, 329, 14063, 16923, 14611, 24740, 28035, 22443, 22270, 3919, 2344, 26112, 20576, 11845, 2486, 3056, 2104, 31544, 16498, 2508, 12878, 28412, 6723, 28002, 22411, 4193, 23117, 31583, 1227, 4514, 25290, 28875, 20801, 3857, 16544, 20236, 20012, 16274, 5433, 29346, 24401, 28534, 21135, 29072, 27857, 437, 5665, 2109, 3671, 8722, 798, 32716, 24540, 2906, 10282, 1848, 20366, 23290, 3227, 14458, 5616, 30865, 6305, 30813, 25434, 1770, 9555, 8507, 4299, 7730, 4075, 8803, 21439, 26194, 22488, 2224, 28728, 32120, 25358, 2280, 6052, 19764, 23713, 24013, 12452, 28374, 24978, 29964, 12704, 31897, 27558, 10377, 22837, 2115, 19459, 20432, 20955, 22105, 20526, 5937, 1366, 3914, 23155, 19290, 6446, 18182, 19635, 20093, 3936, 15846, 21238, 5456, 24403, 28254, 25599, 3641, 6925, 18074, 12324, 10344, 3468, 13749, 30469, 10597, 14334, 16647, 22426, 20043, 28420, 13592, 31494, 17269, 5175, 32296, 28690, 19481, 14471, 3890, 16713, 1290, 16030, 10806, 736, 29360, 2688, 5314, 26950, 6391, 4736, 606, 391, 2093, 13196, 20583, 17950, 17557, 7435, 12, 27672, 10869, 10165, 30351, 4620, 1439, 2516, 31162, 4515, 3770, 24243, 30583, 2497, 1367, 11091, 30942, 18563, 9125, 7007, 18370, 15121, 31468, 2658, 24336, 18275, 1221, 11576, 1799, 29837, 14241, 2284, 5712, 3884, 23635, 23263, 17830, 28346, 26540, 27337, 4714, 27650, 3339, 23705, 14670, 15940, 27656, 2080, 7728, 27421, 32673, 13, 13124, 4692, 9354, 2400, 5494, 4261, 29006, 17554, 20700, 6005, 3418, 5334, 16794, 21026, 20463, 528, 5458, 30494, 21351, 20199, 3, 18442, 19280, 10016, 28028, 15098, 30286, 1730, 8553, 3274, 23262, 444, 21649, 29156, 7055, 12572, 12889, 2858, 28324, 129, 31586, 27443, 8046, 27825, 5044, 32617, 25936, 7473, 29906, 333, 8756, 11051, 31548, 16534, 23931, 22322, 829, 8202, 11176, 21580, 13269, 5388, 3429, 15885, 22002, 31386, 16327, 14274, 5760, 99, 25042, 3055, 32689, 4832, 4928, 24572, 3054, 27703, 2570, 28207, 26201, 5362, 4088, 1997, 11652, 19175, 25750, 10678, 30891, 20893, 10630, 29197, 9531, 10489, 15165, 4521, 30458, 9964, 440, 32231, 4183, 22710, 20488, 13763, 4118, 8188, 23821, 17264, 28809, 9754, 14722, 27850, 5854, 6586, 233, 9524, 15818, 1113, 3932, 6472, 27798, 4497, 18341, 2574, 6500, 25566, 29969, 5754, 1552, 850, 31593, 13226, 23486, 31685, 7737, 2730, 17901, 22749, 15967, 3935, 4265, 32503, 5679, 16710, 14508, 328, 26279, 10184, 18121, 29307, 26192, 29636, 5217, 6337, 24972, 10518, 2073, 29003, 1281, 2662, 1850, 2068, 9451, 2972, 8804, 29343, 28179, 21564, 10504, 12837, 11453, 30100, 16333, 32182, 789, 5413, 31279, 13257, 88, 6129, 20297, 31327, 31957, 1778, 15343, 5876, 30317, 31428, 15648, 13613, 253, 28540, 9693, 6332, 1339, 1814, 5489, 216, 23212, 6834, 3849, 6147, 20912, 10141, 23209, 1349, 9180, 32065, 31353, 4562, 23365, 25111, 12817, 11011, 763, 29839, 6516, 13345, 5355, 30487, 30963, 18664, 14442, 1361, 15833, 4655, 6502, 31948, 1481, 29987, 11007, 9006, 30659, 21129, 28724, 21070, 29485, 27104, 17541, 3992, 28230, 22715, 20944, 22178, 7658, 5384, 7738, 31481, 4638, 32271, 13635, 32263, 19782, 6262, 6028, 2021, 22425, 17752, 9204, 5446, 19412, 13004, 8045, 27536, 1244, 2549, 954, 8529, 20132, 9, 22316, 2429, 7761, 9133, 12930, 10746, 28793, 25283, 4845, 4412, 21880, 8835, 24987, 20570, 26560, 7629, 5560, 3502, 26391, 31217, 28249, 28386, 18621, 29953, 26912, 10713, 22334, 16999, 13137, 25615, 14164, 6053, 23511, 27508, 6846, 20943, 6737, 32507, 27016, 12251, 24351, 30878, 16052, 30437, 24420, 28587, 14360, 3778, 15273, 8034, 26104, 28980, 9301, 378, 25387, 29080, 13500, 6615, 12709, 24072, 25061, 19952, 10970, 22823, 27662, 6566, 22050, 1610, 27431, 12978, 26507, 7161, 4821, 27864, 22298, 27002, 16933, 29451, 29193, 21072, 7468, 8703, 12864, 4892, 1849, 9050, 2696, 28758, 22444, 12155, 513, 768, 8885, 4358, 25944, 9847, 6666, 29396, 14535, 26549, 2428, 17633, 19351, 29823, 18625, 27026, 15346, 3200, 20838, 104, 26649, 3217, 23624, 32031, 14815, 31849, 8352, 12019, 6070, 31505, 8919, 28826, 24487, 7030, 22345, 29796, 2134, 5790, 31653, 21685, 16115, 25321, 26079, 2638, 7804, 25573, 10733, 11452, 30616, 12229, 30253, 22159, 9983, 5772, 3845, 13950, 6826, 8591, 30894, 23213, 22371, 11986, 14662, 15190, 27806, 21906, 5418, 21622, 1841, 29893, 16218, 27317, 5085, 9566, 30557, 23279, 4628, 4867, 21345, 31187, 17430, 4155, 6025, 1534, 31457, 5972, 16235, 10287, 2535, 32749, 3348, 16624, 6838, 11918, 12800, 17244, 6431, 9086, 26840, 11508, 21753, 11597, 23192, 5278, 28436, 8907, 28814, 5022, 20561, 32376, 22052, 5251, 1523, 3132, 28075, 1149, 15061, 2636, 30667, 12300, 2, 3410, 2129, 24612, 13682, 23876, 6157, 5289, 28492, 31029, 1776, 30380, 20842, 19131, 26459, 8700, 11638, 30923, 28072, 2545, 29879, 4363, 14141, 30890, 20834, 5832, 6470, 23082, 10000, 14560, 26497, 6141, 16099, 28923, 29581, 6833, 30676, 3038, 28556, 30797, 10319, 5984, 1326, 4385, 24417, 3616, 8125, 26214, 11410, 2686, 26885, 26547, 4969, 5344, 2827, 14643, 10155, 12464, 14537, 5558, 31055, 16911, 3650, 19947, 31722, 9129, 827, 14080, 85, 8764, 7234, 17076, 31995, 4365, 13384, 24847, 28481, 20547, 5555, 10832, 1035, 5092, 27775, 27884, 5859, 27294, 2059, 15653, 7701, 3104, 4404, 15729, 19215, 26232, 25763, 25532, 3083, 3990, 18296, 24439, 31168, 12238, 5241, 20803, 6006, 22620, 6950, 7849, 19205, 4524, 25078, 11787, 27906, 12629, 30473, 7898, 19973, 27986, 27620, 21002, 32196, 24485, 15063, 22211, 20066, 18827, 9583, 3930, 867, 30479, 29112, 8831, 2541, 26457, 11812, 3256, 15802, 1448, 6513, 439, 18497, 1379, 3215, 12293, 6225, 28425, 18418, 16338, 22424, 70, 11183, 6345, 9328, 386, 7483, 19221, 27111, 26917, 5681, 10923, 31781, 15905, 15847, 25731, 2786, 1323, 14409, 10234, 12500, 1970, 30201, 21157, 5668, 3856, 4485, 27527, 5372, 4733, 7087, 11383, 9827, 24499, 3061, 22090, 26828, 12100, 27197, 934, 10906, 6885, 17245, 13209, 202, 19981, 20939, 9699, 4471, 9407, 4567, 5592, 27457, 11427, 3731, 25560, 27259, 15452, 24800, 603, 29359, 26851, 6875, 11569, 29158, 16990, 27279, 3946, 7857, 11319, 11875, 24149, 32525, 25610, 10798, 19467, 32414, 20509, 4480, 4880, 7981, 23207, 7719, 6543, 27022, 26654, 20770, 6423, 6920, 12355, 24232, 5765, 18618, 30543, 16237, 2681, 709, 5122, 6964, 6471, 12541, 4301, 27438, 17427, 567, 2910, 19696, 14824, 19683, 5529, 10640, 31910, 19570, 32167, 23535, 20903, 19445, 29473, 15414, 473, 22917, 11630, 31744, 12432, 32139, 24090, 18461, 132, 6635, 21517, 1097, 9321, 1818, 21805, 4333, 4721, 15246, 4970, 15788, 855, 27778, 17227, 26024, 30704, 5675, 30378, 20050, 14642, 19254, 4789, 1390, 18724, 28936, 5840, 14754, 3171, 14022, 32555, 17267, 22509, 2410, 11872, 16552, 15874, 16977, 13697, 32081, 9941, 31442, 12341, 28723, 24968, 25065, 1816, 18655, 16727, 1412, 8283, 2137, 30450, 1051, 1162, 12882, 14035, 3098, 25135, 8930, 28776, 17520, 27950, 4200, 32372, 1148, 25233, 5603, 17125, 6122, 12782, 20760, 17065, 6452, 2787, 5423, 23322, 4486, 3435, 14638, 8631, 1565, 6426, 4362, 32359, 8378, 14419, 6387, 8741, 25315, 13076, 21168, 31637, 27915, 19587, 20531, 30062, 29567, 20552, 1378, 30911, 20781, 3742, 9118, 2900, 4254, 24234, 162, 10680, 9790, 29290, 17399, 8168, 27568, 19634, 31157, 7544, 2444, 5275, 20800, 25219, 5088, 16349, 3519, 7580, 29465, 21448, 30159, 31485, 3710, 23340, 26849, 9770, 19297, 21433, 15140, 2622, 14545, 5540, 27746, 18250, 21082, 3838, 2691, 25531, 17730, 8396, 25446, 16331, 30743, 26868, 25409, 13139, 29064, 32693, 6676, 10143, 5162, 17973, 1724, 27012, 26532, 1977, 20433, 10843, 5286, 30297, 21298, 11471, 8937, 11261, 12809, 27786, 23586, 17327, 31177, 9053, 3323, 7155, 5491, 24597, 7099, 31394, 21823, 31830, 26466, 2213, 23993, 15090, 21653, 21234, 29999, 24247, 6755, 2065, 10745, 619, 23488, 3022, 23377, 26881, 30165, 28719, 20805, 2794, 7646, 25740, 13441, 5746, 1756, 21797, 12066, 6460, 20391, 5236, 26266, 1270, 27227, 27998, 16396, 31148, 2932, 2615, 32736, 17015, 22893, 19323, 28606, 1124, 4218, 27576, 26891, 31413, 1843, 2698, 28064, 22729, 1494, 24037, 24195, 3830, 32460, 5300, 1701, 10794, 18561, 13303, 14992, 13959, 4948, 19206, 14613, 3670, 15639, 19455, 6561, 2651, 4410, 7384, 16576, 22539, 32339, 25364, 6936, 27719, 6849, 26755, 13912, 29297, 5714, 5371, 16808, 27941, 18981, 13156, 23298, 4874, 6871, 29519, 19466, 2172, 19448, 1123, 13531, 10204, 29611, 2029, 29630, 30513, 1951, 7320, 6338, 21845, 31810, 30368, 6328, 25416, 6100, 14399, 21567, 7046, 759, 8941, 983, 852, 4898, 10974, 27013, 31243, 6020, 16145, 32434, 13659, 9324, 767, 32681, 3968, 15927, 2094, 10849, 6565, 4312, 17923, 31566, 5417, 15411, 17368, 12048, 15781, 23833, 10574, 24688, 21884, 7668, 28307, 2425, 26148, 6630, 5708, 14960, 6700, 23922, 19951, 1492, 10342, 630, 3425, 10430, 19451, 17720, 10198, 3327, 6281, 24405, 21382, 26861, 21614, 16612, 25214, 27601, 4009, 14938, 25005, 1678, 16666, 6985, 26906, 25579, 28706, 29619, 29180, 1935, 12733, 23251, 10724, 23076, 15292, 8159, 5778, 15596, 9527, 10807, 30387, 12027, 1920, 10805, 11127, 27758, 4547, 16583, 9803, 28124, 32239, 21341, 5635, 3465, 30183, 22278, 4927, 16434, 4711, 22229, 8853, 21930, 5408, 14730, 32543, 21632, 19141, 28992, 15416, 3063, 25545, 18815, 26876, 1373, 17974, 1741, 21885, 1408, 32652, 19748, 23085, 25172, 4974, 8171, 7380, 2277, 24796, 4172, 30266, 20731, 8987, 11670, 3525, 26158, 25689, 21727, 13746, 19688, 5352, 9197, 20989, 17895, 398, 4951, 12018, 19373, 26663, 32570, 23873, 30140, 3068, 26392, 25945, 11859, 26608, 2502, 22839, 17583, 13518, 9159, 21518, 27974, 6167, 31248, 28457, 27880, 1143, 7193, 17882, 2796, 26929, 30770, 14557, 3780, 18554, 3078, 29537, 16868, 24521, 25618, 29932, 26381, 32522, 13419, 14345, 26169, 31345, 22216, 895, 23239, 23725, 25779, 3475, 27172, 20421, 8716, 21762, 30200, 3157, 4738, 32413, 4605, 31483, 3799, 25716, 7363, 13253, 30995, 29228, 745, 5544, 3310, 18593, 27369, 13362, 5606, 3666, 28500, 6219, 21671, 1635, 7571, 31981, 25940, 24014, 2914, 3212, 7507, 22622, 8327, 32008, 20320, 1982, 19898, 11271, 18386, 2192, 8552, 16217, 31214, 14988, 693, 15089, 28474, 22414, 12296, 20268, 30478, 357, 29949, 2746, 11568, 30921, 25130, 4652, 26456, 7237, 30428, 4916, 25298, 4549, 12546, 19039, 29753, 5027, 30515, 11431, 7494, 691, 31460, 2845, 1121, 4416, 7774, 17056, 29118, 6872, 6764, 1008, 6896, 30917, 15132, 32384, 6822, 5779, 4, 25491, 30256, 26370, 18519, 4868, 5216, 7901, 8361, 4255, 23966, 1974, 31454, 362, 14780, 3869, 4780, 23289, 21713, 4941, 3279, 29444, 28304, 12840, 5291, 12135, 15632, 19582, 31239, 13250, 12644, 8852, 27170, 32009, 7338, 27552, 26908, 21894, 10419, 32621, 30244, 30044, 16782, 2591, 17436, 16825, 21052, 25060, 24, 5845, 13296, 31610, 23773, 4847, 4305, 16677, 28918, 21111, 5281, 11650, 11570, 21980, 26681, 7426, 1459, 31267, 5797, 6118, 17718, 3777, 30285, 16065, 7260, 5822, 4371, 1287, 26155, 1987, 5356, 24885, 5625, 30909, 7780, 6367, 2242, 17148, 7714, 29609, 15620, 4617, 29966, 9624, 11781, 26057, 12706, 26508, 7482, 12547, 4458, 27397, 1905, 5118, 1606, 8414, 15601, 29597, 18294, 1967, 2189, 27339, 10039, 16163, 28734, 9774, 18444, 6150, 27341, 8080, 26770, 1979, 20449, 14086, 15674, 1257, 14185, 6376, 31829, 12275, 26090, 31294, 2211, 15037, 5438, 8962, 16591, 5199, 31377, 13612, 4870, 16876, 7276, 19958, 15171, 24191, 23248, 7303, 12698, 22550, 5758, 5728, 27635, 23742, 7438, 11860, 11473, 24477, 11493, 695, 15404, 17213, 10032, 5525, 31257, 27066, 4421, 5848, 168, 3287, 29910, 3296, 14838, 19232, 32303, 5546, 23566, 5643, 17262, 18534, 30240, 29868, 14694, 6102, 17011, 7733, 10642, 2269, 3834, 3547, 9730, 1503, 6267, 26365, 10411, 3526, 14737, 31161, 31038, 30031, 8117, 16317, 11999, 12151, 24991, 7538, 6782, 23454, 27077, 12012, 3912, 31209, 23837, 22174, 7091, 6057, 23718, 1851, 30516, 6876, 1106, 28722, 3486, 29508, 8001, 28516, 7747, 4728, 3029, 9921, 27582, 15863, 29806, 9488, 29831, 16714, 28844, 31694, 15082, 25736, 3839, 12619, 27983, 19647, 19362, 12406, 24560, 4033, 6187, 12561, 22720, 24380, 17441, 6550, 26111, 21738, 2414, 30015, 1005, 32251, 2989, 32281, 1482, 401, 653, 23380, 31971, 27244, 12606, 23058, 19787, 7356, 6327, 11430, 17622, 10747, 13407, 24296, 23714, 7205, 263, 15406, 18810, 12515, 21935, 6907, 7785, 18334, 29569, 25001, 1476, 9963, 14177, 15726, 29634, 11707, 23529, 9266, 23627, 28764, 19900, 3248, 30590, 17530, 7757, 21413, 19096, 17016, 8092, 2248, 7251, 31007, 13730, 3050, 5547, 4452, 22255, 14684, 8535, 594, 29781, 7790, 18036, 10047, 8505, 10607, 3278, 4958, 16281, 17746, 5663, 14422, 24592, 6413, 12808, 24052, 3411, 29938, 2948, 16924, 10657, 6708, 23946, 5335, 2010, 2263, 1627, 17831, 6441, 8062, 10637, 29899, 2023, 10909, 7138, 4654, 25698, 6988, 8827, 2372, 19046, 2139, 1234, 8081, 898, 19792, 15221, 18697, 30727, 813, 6206, 32765, 25007, 351, 4816, 20194, 16238, 23988, 30231, 31827, 421, 15980, 25743, 2383, 11099, 4756, 28205, 2466, 23668, 9359, 6395, 31572, 3354, 15094, 24332, 21866, 1888, 15266, 9463, 2346, 17431, 1286, 13360, 23166, 3887, 4561, 27691, 13883, 22173, 14966, 15706, 4391, 2474, 10708, 25929, 23670, 10820, 22266, 28478, 21378, 13927, 23038, 4340, 13359, 3225, 11978, 4030, 27361, 20191, 29328, 28807, 1444, 2809, 30156, 6040, 7992, 28864, 647, 10150, 15225, 29639, 4857, 22969, 9740, 16001, 30007, 305, 9526, 32059, 7346, 16979, 22966, 24478, 5345, 29053, 18886, 396, 14720, 24788, 26521, 4442, 856, 28284, 27106, 6817, 10795, 3709, 8799, 5999, 3795, 4246, 21252, 21367, 24140, 18103, 10855, 16755, 11052, 16856, 8445, 7776, 29161, 6166, 10789, 28908, 4705, 19127, 4447, 14427, 13503, 1276, 14265, 6675, 5246, 19781, 17803, 1345, 24263, 22760, 9316, 8115, 13777, 8069, 21828, 4784, 22703, 4444, 6017, 18374, 7651, 7535, 4402, 13935, 4441, 7334, 5942, 29167, 21414, 31387, 21857, 12912, 7859, 27643, 17691, 28720, 25782, 25815, 3144, 12133, 4511, 32304, 29747, 5231, 23206, 19098, 7987, 17619, 16791, 32370, 10467, 29162, 5509, 2071, 6975, 4653, 7694, 30678, 5465, 14762, 27427, 27990, 17460, 26893, 6173, 29335, 9122, 26166, 23948, 7151, 5119, 5138, 5466, 11373, 6347, 26061, 9341, 7693, 4149, 10406, 7521, 14268, 29696, 6065, 6619, 10127, 18571, 13114, 18042, 7750, 8362, 1728, 24746, 9977, 4353, 14025, 24861, 20465, 26764, 13953, 29821, 11626, 6778, 165, 16545, 25357, 15780, 31124, 8011, 5815, 11347, 20723, 4716, 4699, 31079, 31768, 14426, 6440, 30468, 21291, 9029, 5342, 21598, 14978, 13727, 12825, 20460, 1919, 5180, 12223, 17347, 6402, 22165, 25399, 15423, 14055, 28152, 8807, 18809, 21330, 6091, 18111, 26450, 9344, 3075, 17689, 23205, 27744, 395, 11217, 9856, 7722, 25479, 26435, 21650, 12088, 30307, 21067, 15778, 27543, 3843, 9048, 5932, 27779, 26091, 31626, 24915, 19616, 7548, 10980, 12166, 21661, 1186, 13933, 3929, 24085, 15250, 891, 31004, 23288, 27015, 28494, 24057, 12559, 4534, 6910, 25179, 21290, 21241, 10264, 29896, 5788, 2407, 5864, 25335, 26437, 26108, 20158, 25700, 31292, 20042, 29115, 10163, 25424, 6396, 16250, 32203, 26586, 5409, 4840, 31931, 13437, 1038, 6059, 18001, 17041, 9845, 23630, 27074, 10400, 8993, 2054, 31180, 4950, 5542, 1872, 8318, 8581, 2643, 21827, 18201, 4502, 19023, 12038, 26966, 23834, 28763, 30371, 32581, 4732, 11456, 32644, 11210, 29089, 12593, 4477, 23792, 27191, 6990, 30519, 2775, 30284, 12069, 7167, 27268, 1835, 4306, 26544, 29691, 21069, 7573, 11444, 28200, 4195, 23336, 6183, 24299, 4316, 28913, 27158, 16764, 9948, 803, 13772, 7008, 13790, 4956, 5047, 831, 4888, 7499, 6710, 7467, 21814, 27428, 15467, 15605, 5511, 22383, 465, 26351, 4826, 24661, 11929, 7440, 32502, 4359, 13886, 32128, 9047, 22153, 30503, 24281, 21605, 4709, 22971, 2300, 7242, 2332, 4674, 8253, 23225, 29229, 7554, 16588, 5351, 17252, 15857, 2706, 20975, 3273, 11351, 1947, 30741, 26708, 2595, 72, 17290, 7070, 13717, 32146, 23760, 10756, 4092, 26872, 7057, 17032, 26898, 32586, 29468, 7933, 20444, 6721, 31027, 18025, 8232, 4000, 22332, 23022, 9145, 5926, 17709, 20895, 30501, 29585, 9319, 7513, 7136, 27488, 16244, 4865, 12490, 3375, 6546, 6326, 936, 14565, 18962, 7059, 3719, 31197, 9982, 8113, 25122, 29157, 766, 7633, 27566, 18321, 636, 24973, 17995, 28081, 27787, 32562, 26729, 8056, 6133, 5477, 28624, 57, 1335, 20461, 31453, 2577, 18916, 3163, 26538, 23378, 29093, 17334, 9588, 5424, 470, 26769, 14115, 366, 28006, 15232, 19874, 19078, 4337, 16800, 12848, 24609, 5958, 16757, 23671, 19058, 624, 29357, 7009, 8651, 1766, 27095, 20204, 19602, 5147, 8087, 8643, 5069, 22978, 29549, 8580, 1072, 12473, 2119, 32309, 26918, 31444, 571, 1152, 7782, 4321, 14616, 10223, 12712, 11186, 3141, 4766, 27467, 268, 4433, 10876, 4662, 19946, 16981, 11753, 4704, 10838, 8597, 4322, 19595, 8911, 21115, 28593, 28013, 237, 26765, 5578, 26265, 1864, 20566, 26556, 3928, 9365, 2755, 7184, 25356, 1108, 26920, 12000, 25784, 4389, 1409, 1419, 8957, 7817, 418, 27322, 13686, 7943, 5035, 3112, 8231, 29202, 17049, 28693, 20401, 21435, 3546, 3983, 17576, 27155, 21708, 90, 1164, 31682, 19725, 14107, 5883, 2449, 2377, 1094, 1417, 15355, 1207, 29392, 29384, 8611, 32606, 18403, 31935, 25487, 13041, 5660, 1273, 25282, 7189, 22032, 7223, 7787, 18178, 6795, 7560, 11137, 24536, 2463, 22177, 5863, 10203, 21913, 2895, 15314, 14099, 22998, 8103, 16598, 20117, 24201, 941, 17374, 28788, 6524, 14106, 29491, 8848, 2006, 28861, 8235, 27570, 32661, 15471, 3291, 2030, 18920, 16845, 32541, 20759, 20598, 7135, 22808, 8372, 2066, 26823, 13505, 31384, 3390, 15311, 24252, 20017, 6506, 28306, 15543, 20848, 24594, 30355, 20057, 18863, 31978, 28206, 14668, 24770, 23386, 12610, 26273, 6339, 10813, 1283, 8358, 30035, 25295, 31192, 8875, 6372, 4876, 27298, 7559, 6686, 2142, 19077, 14784, 19275, 31438, 2113, 14840, 16048, 26038, 30390, 8775, 24493, 31988, 26394, 1591, 5150, 4142, 8086, 3734, 1452, 25432, 25693, 30219, 3356, 4855, 15304, 22378, 1391, 28755, 13260, 2725, 22826, 4289, 31974, 27128, 27632, 24409, 28823, 15719, 27055, 18614, 9428, 6487, 25223, 8026, 30872, 17727, 21709, 24578, 20565, 147, 29759, 14473, 18976, 21750, 8653, 11953, 15229, 4509, 5343, 18600, 18771, 13611, 8778, 27627, 16188, 1568, 15002, 17960, 6968, 30000, 28816, 12360, 29217, 22385, 4001, 27391, 11619, 22946, 2824, 16696, 12389, 6894, 7019, 7335, 25030, 18627, 6307, 25551, 19295, 8874, 23643, 31853, 12747, 7965, 25322, 6512, 8754, 15953, 21792, 12110, 12197, 497, 31991, 4112, 25652, 28988, 27289, 11231, 23034, 4743, 414, 6438, 20656, 13265, 3483, 4717, 19924, 28475, 18972, 20802, 13652, 27299, 13580, 10925, 20380, 16708, 13599, 27075, 2128, 4990, 28955, 8095, 29134, 3559, 14658, 5140, 3156, 26094, 77, 29673, 12959, 10530, 4905, 2676, 2417, 10638, 24554, 27394, 10276, 32456, 25232, 12328, 2722, 29063, 15385, 4139, 27816, 3018, 4487, 11304, 14368, 2690, 12975, 4813, 1183, 10391, 31887, 32415, 26139, 23733, 24479, 11773, 4906, 8513, 22495, 552, 18835, 31541, 19652, 23171, 18595, 26724, 7273, 28517, 5784, 24142, 21163, 11612, 10474, 13649, 31435, 30670, 1720, 583, 13833, 3127, 2045, 1330, 21985, 8322, 9772, 28609, 3771, 12793, 11696, 14373, 22061, 26715, 24141, 25308, 16739, 31138, 13497, 30251, 26398, 19289, 3772, 5693, 26474, 12999, 7478, 7366, 5965, 6998, 6352, 13567, 3577, 13854, 4815, 26066, 3965, 8590, 23956, 29849, 26892, 2548, 4252, 6814, 11881, 8347, 4670, 5221, 22318, 18098, 21967, 7972, 3572, 6067, 29586, 23797, 1789, 24413, 32227, 29226, 30572, 16812, 8434, 30169, 17575, 24874, 21890, 47, 7385, 25420, 32361, 6484, 2042, 6214, 4564, 11500, 10298, 26596, 280, 6776, 14923, 26496, 7460, 5274, 22677, 23023, 260, 18764, 31825, 24820, 25591, 12919, 12855, 297, 15727, 4665, 10300, 21888, 14969, 21813, 30373, 26041, 20359, 3280, 20456, 2037, 2243, 8428, 3913, 420, 16882, 3032, 23266, 16387, 20631, 7906, 28921, 15380, 25912, 16702, 27078, 9653, 8594, 4677, 31779, 4406, 17802, 29834, 19168, 27969, 371, 1137, 3092, 772, 7862, 5320, 3564, 9019, 4685, 26677, 13117, 32165, 19763, 10648, 4403, 18667, 5426, 7620, 213, 25676, 15707, 12075, 3266, 16729, 18482, 29466, 30665, 12494, 22571, 28990, 31512, 4443, 5633, 25947, 19224, 16778, 4594, 6525, 18740, 8303, 30294, 26290, 3058, 18589, 12270, 24633, 3415, 4788, 21563, 29860, 25622, 19514, 28276, 6554, 22711, 17375, 18284, 30966, 6258, 23564, 14394, 32374, 3642, 12415, 14096, 4344, 29528, 7397, 7401, 31103, 29295, 26417, 8795, 31015, 25082, 3673, 24222, 18388, 24275, 25123, 22372, 7290, 1396, 4682, 771, 2585, 19529, 25881, 1447, 32718, 3939, 24763, 19615, 2072, 793, 14712, 1260, 13631, 28531, 17454, 7971, 27596, 2209, 28832, 20925, 1289, 2431, 18964, 29715, 30929, 21289, 19207, 29629, 28167, 5894, 10341, 23980, 31352, 31956, 21426, 19786, 31169, 18822, 869, 8258, 2993, 800, 16804, 27940, 7089, 8949, 22608, 2511, 5630, 23637, 15937, 6605, 24366, 30426, 30047, 3745, 11160, 24376, 11425, 10090, 7932, 32536, 32341, 20795, 27537, 6018, 25136, 31709, 6655, 10371, 2114, 1713, 4375, 15717, 13965, 4539, 19501, 25590, 29233, 28502, 10053, 6002, 4431, 32197, 17089, 6434, 32016, 2164, 32699, 10812, 13627, 8984, 22755, 6730, 5521, 10449, 3876, 6394, 2415, 5741, 30049, 26574, 16019, 2472, 30116, 29633, 14779, 11343, 8731, 31861, 26753, 5096, 24654, 7634, 15138, 8963, 26546, 4960, 12413, 17781, 5113, 3469, 8286, 7277, 32506, 29013, 23, 24726, 29388, 14799, 15961, 28705, 23632, 28647, 7214, 24104, 11656, 1272, 8373, 32430, 15031, 27481, 7253, 25252, 32686, 26725, 20036, 32563, 5056, 32363, 7546, 6164, 24818, 30638, 648, 2506, 1933, 26465, 24703, 20089, 12242, 21704, 23894, 17940, 31729, 12065, 30115, 23679, 24538, 29671, 26356, 625, 25444, 17156, 4104, 6363, 30960, 26256, 4413, 27567, 9726, 1788, 22085, 2181, 4555, 5256, 4188, 12716, 838, 20998, 11915, 17499, 12222, 6733, 8204, 17629, 8881, 16916, 25786, 19661, 20790, 18424, 6485, 10447, 29637, 26711, 21081, 9131, 27017, 2105, 9155, 3749, 32412, 15991, 25438, 3302, 3317, 9345, 26527, 7270, 32725, 27076, 23845, 21202, 3948, 9095, 7396, 16255, 31049, 30941, 9113, 1586, 15546, 14278, 4027, 3903, 4724, 8465, 15986, 13365, 11397, 15868, 17616, 25875, 8344, 20604, 26015, 10879, 16015, 19372, 3635, 24339, 3011, 7326, 14017, 10180, 6855, 19869, 22536, 15458, 3700, 15622, 7274, 5640, 31469, 13914, 24027, 2708, 22427, 8592, 3921, 25506, 23373, 29428, 18043, 29835, 29907, 31498, 15828, 28178, 22218, 10802, 22943, 31944, 5853, 24845, 23275, 5403, 8502, 31488, 22685, 9711, 1900, 7254, 21354, 9942, 9481, 1500, 27665, 15866, 5209, 1377, 6398, 17500, 31314, 1916, 6685, 28393, 3253, 32352, 30735, 2101, 16111, 28815, 22043, 3960, 30141, 32037, 6583, 4285, 15203, 19712, 3981, 19084, 24106, 15807, 25348, 21104, 10584, 30258, 23998, 4372, 32038, 10560, 9087, 2715, 11945, 29592, 2621, 3723, 11465, 24361, 27948, 7033, 31916, 1957, 1496, 29703, 31463, 6858, 4683, 13460, 1714, 12444, 23479, 611, 9924, 31836, 4625, 27027, 1667, 29098, 24092, 14817, 12218, 6720, 4266, 21379, 10469, 31496, 5475, 9637, 24495, 5187, 13517, 26460, 32195, 28418, 17501, 17235, 12625, 196, 9947, 16006, 17388, 20917, 1243, 30234, 9114, 24569, 1571, 8027, 6194, 4466, 29825, 7602, 9175, 15390, 23442, 27203, 24186, 19460, 5128, 4436, 25793, 16491, 24197, 30411, 23485, 7413, 8498, 22223, 2790, 517, 7125, 4221, 27210, 24863, 29758, 1754, 6163, 31134, 25870, 29419, 30298, 2654, 27645, 5181, 8918, 9453, 2656, 20714, 22356, 14019, 23610, 6127, 4730, 17401, 18826, 12892, 31208, 16024, 27229, 10755, 7088, 14619, 20288, 12384, 24799, 21756, 15635, 7661, 2199, 1410, 4209, 29398, 29746, 6914, 30926, 31889, 10302, 16234, 24012, 29435, 10263, 24602, 22092, 28267, 14881, 12948, 12040, 23761, 27557, 4796, 26603, 7310, 30604, 14020, 26718, 30937, 31867, 3001, 22014, 2007, 6673, 32498, 28767, 12068, 18985, 7555, 10764, 32546, 4663, 1118, 7147, 32665, 9360, 31419, 20187, 22942, 14497, 9188, 7416, 7883, 7613, 8933, 5348, 22831, 8550, 7596, 11659, 9609, 25729, 4117, 29612, 963, 29660, 18189, 1225, 6799, 4409, 27769, 32141, 2304, 8107, 22629, 19913, 154, 23151, 14050, 25155, 15249, 2868, 13202, 27145, 9011, 18727, 28582, 5707, 21496, 17942, 14972, 22650, 15008, 5819, 5621, 19483, 727, 30215, 13705, 1736, 18158, 29709, 28194, 2491, 5031, 18752, 18983, 20224, 4986, 28134, 13331, 30698, 7858, 4061, 326, 21596, 12728, 26082, 5172, 27136, 19480, 17153, 27757, 16891, 31715, 29096, 31373, 5726, 23899, 5134, 863, 6130, 5721, 30697, 27979, 15017, 27206, 24781, 4856, 2040, 3162, 2050, 3524, 31328, 5736, 17670, 31122, 24748, 6221, 8822, 32713, 29601, 2807, 25312, 32272, 23540, 1882, 1199, 3106, 2594, 477, 6444, 27684, 28686, 7190, 114, 29518, 26738, 20022, 14605, 21887, 1921, 7663, 30343, 5613, 4920, 3466, 19129, 4145, 25006, 21859, 20864, 20479, 15472, 31445, 10408, 3817, 19663, 22222, 30994, 21183, 32368, 29123, 27829, 22454, 19464, 6847, 5993, 28153, 32159, 30843, 11991, 8292, 28757, 26039, 1030, 18249, 21375, 1425, 29327, 22291, 25443, 12771, 9537, 17140, 6888, 7563, 5363, 28432, 3636, 3461, 2519, 23693, 28015, 26896, 968, 13704, 26991, 2193, 28057, 22927, 45, 5306, 25919, 24461, 524, 1546, 9110, 18796, 19484, 22693, 17194, 10605, 17059, 25853, 30701, 15179, 25727, 10782, 1971, 1556, 24276, 27276, 26042, 24943, 26589, 12543, 24220, 8532, 2379, 18590, 25549, 136, 31759, 13577, 17173, 276, 20978, 7464, 29102, 32508, 14232, 20575, 9898, 23731, 1531, 8386, 16361, 4397, 29012, 25379, 8035, 4121, 9813, 5020, 15533, 5585, 28954, 23619, 22035, 20673, 1718, 6692, 1804, 8094, 3952, 4915, 2331, 4569, 11808, 5824, 9025, 32481, 31691, 32569, 8240, 20145, 17330, 8262, 30646, 26149, 24900, 19387, 14614, 22686, 1705, 30207, 32171, 2832, 21044, 14502, 8371, 9435, 8032, 10038, 23965, 10388, 15344, 10439, 31114, 15901, 3988, 25092, 12140, 18232, 21834, 2770, 9765, 24884, 4165, 9119, 30773, 24574, 25804, 29498, 25820, 19530, 16131, 810, 23605, 19797, 15918, 30946, 6996, 3236, 14833, 20618, 2703, 5062, 31259, 16135, 9690, 3420, 2646, 8860, 5803, 31427, 145, 21370, 21013, 9836, 22110, 10068, 6906, 12707, 25158, 25737, 16479, 865, 7626, 18126, 26188, 20646, 30461, 6291, 24682, 32387, 29902, 29503, 6951, 2433, 1532, 3515, 7204, 12862, 22636, 18460, 17230, 2002, 4309, 4568, 23554, 18138, 2328, 7001, 26181, 28828, 3406, 17804, 8579, 26579, 5614, 6370, 37, 22219, 28273, 2458, 712, 31269, 6718, 29988, 9289, 26314, 9033, 14672, 26986, 23120, 1964, 20165, 31280, 31320, 26098, 739, 7394, 15837, 21224, 7456, 818, 1797, 6296, 1091, 27064, 9420, 23615, 25, 6714, 31773, 8091, 23878, 11721, 8787, 27811, 27451, 14383, 15721, 16587, 26670, 1374, 889, 9914, 24358, 11855, 177, 3706, 26120, 9643, 22302, 4072, 23363, 15692, 407, 682, 5144, 13748, 21415, 5347, 22538, 5448, 3122, 25647, 26595, 8879, 11858, 18583, 32592, 1665, 24689, 8288, 17925, 16395, 343, 22713, 2875, 27931, 3143, 5354, 672, 18446, 28941, 20879, 4500, 4293, 23512, 22309, 508, 21585, 2289, 28050, 22992, 14127, 4550, 6941, 16502, 28795, 3501, 16157, 22875, 29600, 24470, 1978, 10828, 16288, 8012, 24323, 9452, 17072, 30446, 27234, 20002, 19202, 13048, 13506, 16913, 2887, 27821, 17784, 8064, 25349, 23806, 924, 4012, 25189, 25876, 21101, 18769, 20809, 7588, 19774, 9315, 31333, 2838, 23568, 28468, 8774, 27314, 32417, 3864, 13720, 17809, 7677, 17905, 23875, 8418, 10900, 9285, 21878, 26436, 23528, 18955, 15739, 30377, 21019, 5245, 5393, 6257, 10792, 7726, 4237, 12359, 31871, 554, 26267, 2958, 8018, 28208, 2385, 8889, 30305, 8846, 4783, 15794, 8463, 30009, 23438, 1961, 16397, 14361, 7485, 13095, 599, 23284, 23618, 25047, 6624, 16056, 20892, 12250, 28897, 2420, 9431, 22666, 6390, 5034, 22882, 6254, 27957, 947, 27021, 25954, 908, 16834, 5837, 9787, 11048, 23331, 22796, 31043, 7881, 4981, 23926, 26940, 7067, 29310, 3696, 14229, 23319, 1529, 25662, 6703, 2460, 14731, 25248, 2158, 30764, 28805, 29805, 6013, 27042, 1120, 8073, 19220, 29131, 3555, 25338, 24982, 16000, 28668, 16411, 17008, 16917, 20202, 3782, 26820, 3295, 6629, 10699, 22952, 18596, 16884, 9738, 2392, 26130, 16603, 7257, 17403, 32355, 30280, 7968, 1042, 8212, 19245, 10138, 5897, 27306, 10860, 21989, 13770, 14749, 9297, 22559, 29623, 3252, 20321, 13740, 19713, 4523, 26296, 26109, 5487, 4240, 2664, 3552, 20543, 29173, 5580, 21407, 23054, 5370, 30889, 28872, 31570, 28105, 6760, 6598, 15772, 19331, 28117, 6010, 30366, 12041, 24386, 1358, 22429, 13082, 27773, 8385, 10463, 6536, 10331, 9764, 24724, 30637, 8605, 8108, 22583, 18847, 31737, 21452, 13696, 28344, 1233, 6377, 10456, 4271, 28465, 29272, 24782, 84, 31999, 22954, 56, 31872, 3824, 12582, 22351, 1616, 4107, 29743, 25766, 7909, 22843, 22785, 4530, 10072, 7074, 30150, 13281, 32697, 22423, 519, 8740, 7107, 7076, 22432, 2393, 10971, 21587, 11743, 2239, 3802, 7861, 23449, 31342, 31791, 7977, 11457, 27740, 19293, 9497, 19628, 12394, 19291, 23664, 5107, 24445, 14332, 509, 1082, 29183, 4227, 2998, 24751, 19691, 9183, 32005, 20270, 10431, 9404, 17318, 20114, 9416, 3178, 25285, 15152, 25771, 12915, 14337, 12788, 5066, 15177, 29332, 19968, 20584, 27418, 9246, 16020, 30715, 8162, 21745, 7567, 10164, 10256, 29843, 10494, 12154, 16094, 1342, 32529, 28484, 10337, 20339, 29239, 22073, 24583, 10219, 25639, 23309, 29079, 10446, 8442, 1341, 797, 141, 7925, 5687, 199, 26743, 1506, 1294, 8844, 15690, 32747, 3458, 26371, 21288, 2831, 23612, 5064, 2515, 9067, 4793, 3826, 2852, 25533, 2044, 20734, 16620, 32365, 28545, 28855, 19959, 2118, 30445, 17376, 26935, 10453, 4914, 31220, 20826, 15670, 16712, 20880, 12526, 6845, 10460, 22818, 28659, 9164, 26318, 25222, 27992, 30745, 30979, 26590, 29356, 1612, 25133, 22691, 11694, 8130, 22609, 7614, 4503, 17139, 7453, 4762, 30430, 4764, 23634, 1740, 443, 28717, 30208, 11114, 18887, 31857, 22272, 30302, 18464, 9565, 8819, 19326, 25301, 26602, 31684, 15970, 21455, 26375, 17908, 14898, 996, 8518, 27634, 558, 29439, 1068, 9479, 8743, 15399, 6727, 4943, 15651, 26298, 31732, 31156, 22806, 17852, 21660, 5780, 3243, 27511, 25478, 7836, 30291, 19380, 3528, 24416, 25110, 9602, 7606, 27424, 7072, 28443, 32314, 6108, 30709, 6564, 21826, 28444, 7176, 6777, 3763, 2250, 26178, 2160, 9549, 15060, 12104, 1716, 14597, 13188, 7716, 3741, 23046, 6929, 4930, 24301, 3787, 26230, 4106, 27721, 1562, 8528, 20220, 21891, 15323, 28460, 19778, 21089, 28922, 25780, 6904, 27951, 30362, 16703, 11962, 26878, 5596, 6770, 8541, 4761, 8425, 17184, 1431, 27059, 14524, 10018, 12093, 8374, 30680, 7922, 3897, 26464, 23344, 29692, 5753, 12372, 17688, 27230, 13557, 4980, 13491, 4896, 29436, 7739, 3260, 31026, 9610, 20411, 6275, 7026, 29172, 4946, 11921, 21680, 14636, 3036, 17745, 11925, 7575, 28216, 9396, 9599, 29182, 21929, 5851, 8687, 10727, 3259, 28952, 28850, 19150, 4517, 25262, 9667, 1491, 18811, 23601, 30864, 25798, 10742, 19553, 26995, 10840, 32743, 6213, 4028, 2604, 26284, 28415, 27525, 12940, 10197, 28696, 27628, 28077, 28915, 25374, 10397, 28629, 3000, 24295, 7443, 4438, 4315, 10120, 24330, 16897, 9605, 6946, 23616, 5570, 2337, 31382, 9097, 5905, 20192, 10317, 13903, 11230, 2978, 3066, 6719, 13297, 27764, 11528, 10274, 26473, 30724, 29100, 26977, 24205, 27493, 19550, 18390, 16148, 32505, 8254, 14901, 20318, 3905, 6768, 10275, 323, 21155, 23545, 25503, 6335, 25651, 15600, 32687, 29664, 17801, 29109, 15815, 29737, 10531, 1611, 24174, 31881, 6134, 29059, 13674, 16838, 8745, 2969, 7880, 29188, 15315, 13312, 31465, 27131, 22737, 2608, 29604, 2069, 20452, 24658, 26084, 9859, 859, 27110, 24525, 29552, 28490, 10963, 1555, 2081, 8994, 7071, 25707, 2188, 9012, 7725, 28595, 13657, 11718, 31635, 19717, 30160, 14072, 13645, 644, 9671, 13168, 7081, 1171, 9606, 11852, 5386, 24447, 11240, 531, 30239, 12879, 22973, 7659, 26646, 28449, 22407, 11021, 27883, 9835, 20116, 25025, 9840, 13925, 30547, 17120, 26834, 32658, 28467, 9969, 25500, 23943, 13538, 1703, 5156, 7248, 27271, 23368, 16432, 27436, 13047, 4044, 7247, 31313, 30589, 3511, 26327, 12039, 14293, 7196, 20513, 23317, 11121, 22916, 20896, 18328, 1540, 2739, 8415, 1399, 31906, 6184, 6916, 8484, 8525, 6236, 19689, 29179, 27761, 14385, 944, 30265, 14775, 10161, 24646, 3971, 14466, 5235, 26191, 29755, 2443, 26537, 26981, 776, 7339, 1709, 8057, 4302, 2323, 32501, 30762, 27853, 1985, 9780, 7692, 8189, 6979, 28938, 28069, 25900, 4245, 12926, 7821, 21591, 24063, 23984, 24442, 6744, 1962, 5512, 30163, 4772, 19355, 1080, 24268, 19753, 24273, 28849, 28951, 29533, 26971, 30237, 5516, 26895, 20157, 9196, 19418, 22109, 32042, 13318, 18545, 31262, 29369, 11154, 8763, 18254, 31363, 24064, 26014, 7561, 2268, 11358, 23307, 27164, 31799, 23118, 25173, 30634, 13906, 1176, 6385, 26403, 30179, 8439, 12337, 9721, 32148, 19274, 29960, 5036, 28220, 22857, 24047, 20959, 10450, 17621, 18978, 14956, 1134, 17274, 9747, 26599, 14400, 861, 13463, 17255, 3186, 27965, 18968, 10301, 22232, 32759, 4310, 21270, 6274, 22484, 19292, 8367, 28001, 22786, 12102, 11499, 10417, 16109, 26639, 25046, 20638, 21733, 10360, 7770, 1603, 20255, 26469, 321, 32371, 10477, 26687, 9563, 8677, 16031, 24606, 10993, 29782, 27976, 17392, 24674, 17462, 27828, 17415, 3848, 12535, 5814, 22705, 13707, 27561, 24215, 24534, 7496, 23509, 20873, 7830, 22898, 18929, 30817, 13947, 6361, 7360, 31835, 31518, 28398, 15, 9903, 25883, 5731, 4498, 22168, 24927, 7179, 9632, 22065, 30050, 16089, 19980, 18481, 27143, 18880, 32425, 22156, 18261, 6696, 12798, 28045, 32639, 3908, 32072, 3950, 5770, 7404, 25602, 25204, 15450, 21800, 16736, 3536, 8524, 3285, 28272, 16921, 16628, 31840, 10691, 26735, 29379, 5050, 26080, 22601, 19881, 13619, 24627, 12656, 5631, 31795, 22489, 13273, 22422, 26184, 25310, 4069, 1168, 20325, 23100, 2326, 25400, 30577, 7489, 22938, 25361, 31356, 18421, 14039, 8943, 1241, 25751, 10215, 2532, 3298, 27875, 32704, 31967, 18316, 10122, 15925, 16179, 7810, 4791, 3871, 14904, 27321, 100, 29099, 30264, 25226, 10817, 7417, 16391, 19018, 32093, 31891, 3909, 667, 24002, 12032, 17108, 8641, 8538, 32100, 12113, 15693, 25806, 9687, 9062, 24373, 22983, 2217, 20536, 21633, 6523, 22568, 13073, 11552, 29819, 27641, 11928, 24071, 2619, 20343, 8810, 18710, 15544, 6210, 22019, 29647, 8337, 28880, 17034, 3744, 23172, 29457, 13235, 1104, 20032, 15816, 21697, 13197, 2146, 32461, 5805, 2478, 1817, 9547, 19990, 23494, 14124, 4570, 7079, 9016, 28170, 1879, 6535, 5996, 26146, 2960, 28080, 8667, 1437, 15155, 31433, 5757, 14970, 13663, 26638, 370, 14316, 10094, 25067, 16518, 8121, 17664, 17112, 7524, 18934, 9806, 27717, 7209, 20634, 6036, 23353, 10580, 31332, 2526, 21404, 4850, 10944, 22238, 2716, 14061, 19422, 29490, 9460, 703, 13039, 28278, 5852, 4751, 4432, 7985, 17684, 29026, 30739, 9502, 31940, 9564, 27023, 13440, 11711, 4537, 10778, 14781, 20334, 5051, 10696, 2710, 11196, 25021, 6349, 25261, 11350, 6269, 31720, 32101, 3621, 11088, 7675, 29075, 1477, 26446, 32102, 17786, 25885, 27565, 17185, 24020, 5333, 30641, 13394, 11506, 9716, 20091, 9651, 19287, 16242, 16182, 18971, 7241, 16095, 28559, 17301, 6080, 22921, 25241, 27303, 22359, 5830, 30133, 6051, 25613, 21978, 13521, 30783, 7855, 3023, 27179, 1578, 25106, 886, 30605, 8033, 25176, 10100, 22833, 6509, 27390, 31173, 9678, 12953, 12059, 5090, 22277, 22391, 16460, 23315, 8650, 12586, 7807, 32268, 26593, 12273, 13964, 2825, 13270, 21513, 4922, 6684, 8485, 22731, 6311, 10052, 7268, 32315, 9004, 2816, 11200, 20304, 30585, 11204, 32489, 9001, 9143, 24040, 8734, 20344, 26747, 8751, 17191, 19340, 19957, 2279, 10228, 6136, 10796, 706, 355, 12955, 7314, 11424, 20823, 5018, 11687, 24825, 7182, 11951, 19922, 18804, 12190, 26118, 7691, 5798, 24765, 28332, 28887, 1914, 22220, 21917, 20593, 14059, 17657, 8178, 19479, 6115, 28539, 16540, 26987, 23739, 19827, 18566, 4383, 20931, 29341, 8517, 28283, 8410, 1684, 10158, 7504, 31958, 8969, 6895, 31318, 29640, 21714, 32257, 24553, 7522, 8685, 1004, 11541, 1577, 3762, 6253, 4501, 17903, 18803, 7434, 24921, 22709, 6746, 1702, 1255, 25971, 10245, 3516, 31376, 4616, 16227, 28572, 3951, 24302, 10073, 11842, 4703, 16953, 30410, 7160, 9170, 1065, 5522, 3729, 16206, 31212, 25031, 3940, 17915, 9525, 9869, 31350, 6969, 17484, 10145, 18326, 19158, 5311, 21477, 26833, 3321, 3681, 14733, 2350, 26092, 12972, 26325, 3735, 5561, 5756, 5004, 28803, 17570, 27729, 29154, 8549, 12806, 11290, 9929, 10495, 30862, 4925, 22088, 3017, 15804, 14644, 7601, 22341, 4642, 18198, 30488, 24603, 26096, 28772, 1745, 25628, 13589, 29846, 29543, 5189, 8544, 29366, 1175, 27866, 14979, 2855, 9042, 24996, 24858, 643, 31542, 24397, 21373, 21366, 21986, 3263, 7853, 12315, 23078, 19071, 6963, 26778, 26345, 22286, 25359, 17605, 18065, 25655, 12832, 6522, 19738, 18888, 20282, 8476, 13496, 17867, 14302, 17331, 17470, 14320, 6288, 12412, 27162, 6530, 29330, 16902, 23737, 7878, 23565, 31912, 11451, 10297, 30002, 10078, 11274, 31108, 9784, 22305, 29776, 9472, 529, 7568, 12248, 23789, 27955, 27349, 32111, 32029, 28997, 26006, 22838, 19894, 5682, 6815, 2660, 25658, 2985, 26915, 26879, 19806, 29450, 10507, 8197, 28142, 5685, 3808, 10889, 13381, 2156, 20753, 11479, 6418, 5713, 23950, 30396, 26673, 11564, 21180, 14294, 31992, 24003, 16744, 8511, 26634, 2034, 9953, 24392, 26648, 20440, 4234, 11251, 22127, 29042, 8098, 12281, 32117, 8468, 30768, 24107, 27863, 2973, 29572, 30852, 21612, 17481, 28268, 21931, 30837, 17682, 28759, 32611, 795, 29155, 15393, 20227, 3984, 19640, 1618, 7458, 2464, 11762, 32750, 17491, 29548, 7644, 345, 28453, 26714, 1353, 27439, 24127, 464, 10994, 26221, 21940, 8181, 17760, 23777, 11418, 27453, 29322, 14959, 13862, 17818, 30517, 4626, 7018, 10604, 29242, 8690, 30485, 22592, 24513, 26342, 14635, 7895, 6475, 10438, 30636, 30306, 18763, 3724, 18747, 12865, 17088, 18304, 3004, 10240, 30522, 17051, 2603, 9737, 6212, 32669, 27783, 8708, 30185, 23180, 5462, 3472, 11611, 374, 1806, 2518, 32215, 31477, 5505, 10177, 9604, 29306, 22213, 5331, 23004, 3470, 22021, 589, 25892, 5214, 9426, 30597, 6794, 15616, 9306, 17904, 4959, 10527, 2281, 9056, 938, 31268, 31985, 22457, 3267, 13824, 25378, 1332, 22138, 27060, 22473, 11627, 19707, 18445, 29509, 21400, 13911, 17740, 27135, 23293, 27175, 7526, 29587, 7917, 1031, 22246, 4643, 28348, 30033, 25969, 29245, 20150, 2228, 19560, 32734, 32623, 8913, 4824, 641, 9438, 6226, 22933, 11463, 6016, 735, 29049, 11101, 4513, 15408, 30845, 10205, 4435, 11735, 27253, 6152, 24531, 24716, 762, 13213, 17970, 6689, 4347, 18781, 31008, 14455, 23093, 30472, 24611, 622, 25714, 25481, 24143, 4923, 6083, 23783, 22350, 28914, 10608, 7163, 686, 2631, 21818, 15429, 11741, 29415, 7145, 21453, 24135, 2484, 28943, 22476, 27434, 15768, 1258, 9247, 3153, 18779, 5835, 24391, 4127, 13211, 9590, 25930, 5879, 2176, 21651, 27749, 26244, 25735, 20869, 7802, 18313, 15506, 17098, 7773, 27378, 19905, 27209, 10751, 872, 4909, 24758, 31200, 7418, 31638, 24178, 20676, 14183, 22939, 20833, 3781, 31952, 26562, 22865, 16934, 18422, 27448, 9450, 26916, 12336, 29540, 17526, 17346, 28933, 24680, 28238, 20251, 3147, 27487, 4295, 16378, 25653, 17028, 12807, 24824, 2659, 29347, 31191, 5014, 30763, 20852, 7455, 25144, 24850, 26621, 32353, 20642, 11577, 9361, 5917, 23163, 13685, 24576, 20910, 24504, 23108, 2329, 18812, 551, 1687, 31789, 3544, 15744, 4688, 20275, 31641, 24914, 29113, 11062, 25014, 14430, 8808, 16093, 29791, 29994, 17669, 5959, 6773, 15420, 18785, 11575, 26282, 11719, 8696, 15628, 22102, 2777, 400, 2628, 8727, 7803, 3567, 5979, 8898, 16057, 30887, 21822, 21613, 920, 22137, 18221, 1781, 6400, 12804, 17493, 23963, 7748, 24735, 7558, 27462, 17181, 501, 5591, 24880, 1006, 5568, 6392, 4150, 5737, 12196, 10098, 4100, 313, 7509, 14871, 21091, 21676, 8666, 31295, 5639, 32757, 3865, 13321, 4659, 32574, 21032, 23908, 282, 30142, 32240, 23123, 16608, 5875, 21008, 12763, 8190, 10001, 27629, 30957, 23075, 11187, 23436, 2912, 20406, 29867, 28846, 28589, 27693, 24015, 14639, 5412, 17564, 16174, 11060, 10160, 3665, 26000, 4091, 17705, 22779, 1939, 2316, 30730, 9708, 7929, 4650, 14506, 23276, 18904, 27046, 20161, 32017, 4060, 6664, 26025, 24876, 10799, 26515, 17999, 22764, 7641, 23695, 10623, 7983, 448, 10992, 11209, 31458, 29235, 24311, 16724, 25083, 30661, 32190, 21556, 17794, 5310, 2387, 17836, 22717, 28597, 26137, 3131, 21344, 20368, 4279, 21508, 20611, 19427, 5237, 5530, 14991, 26051, 3235, 1857, 20056, 28899, 774, 29417, 12860, 5964, 32754, 26368, 3597, 1327, 5440, 22392, 28419, 5762, 2848, 2098, 21220, 7387, 2613, 25879, 29237, 28328, 7956, 22944, 6375, 10632, 21310, 23870, 5619, 7609, 13266, 22400, 27859, 23404, 7756, 31456, 2844, 7382, 17251, 4407, 9541, 30776, 24126, 23398, 12956, 5659, 24909, 1010, 29750, 24285, 3804, 21536, 317, 4029, 23707, 20599, 18046, 4902, 2776, 811, 13536, 13426, 10096, 21957, 11103, 13288, 25231, 3204, 9493, 26967, 6789, 3550, 11532, 13643, 9084, 18385, 17161, 14828, 10295, 31885, 23064, 673, 32403, 11169, 15154, 6889, 23804, 26961, 21770, 19499, 26486, 12108, 9376, 29627, 3775, 2762, 15634, 3886, 24709, 25592, 596, 504, 28373, 2590, 30965, 6856, 14705, 3238, 30128, 7194, 8825, 23411, 30542, 10129, 24303, 11550, 8540, 1359, 24213, 23325, 27947, 15480, 880, 23816, 1657, 10330, 25033, 27161, 30703, 32597, 16765, 25552, 27959, 6112, 21441, 15357, 13760, 23215, 26988, 27964, 19469, 6864, 5624, 11092, 21383, 23049, 17792, 331, 8985, 21783, 10250, 167, 7625, 4004, 27942, 6695, 26559, 4577, 24228, 21124, 19906, 2976, 19605, 18359, 31510, 29395, 32420, 20103, 21133, 28062, 12877, 17210, 12084, 24677, 11106, 19624, 12780, 23364, 4267, 11280, 25621, 10674, 32019, 22155, 27752, 9556, 22547, 7942, 2456, 13484, 902, 25257, 32090, 23748, 23836, 5884, 8154, 4134, 24932, 16354, 11911, 4542, 5956, 26263, 3881, 14958, 21201, 1304, 6145, 14797, 11861, 9384, 4494, 15825, 26289, 29717, 5441, 4581, 16750, 15872, 549, 6368, 25143, 24449, 19805, 21009, 20729, 28091, 30385, 4116, 23921, 21642, 11816, 25593, 2747, 12903, 26204, 31679, 1581, 25548, 6854, 28657, 19495, 6197, 505, 23698, 27057, 32720, 11286, 10982, 7393, 30573, 2389, 2728, 15743, 18362, 28447, 12004, 26320, 28430, 19586, 13279, 6809, 7564, 3490, 10093, 3664, 26773, 10114, 2798, 28477, 2216, 6800, 26360, 22271, 17938, 1734, 431, 11588, 8954, 24641, 7947, 32531, 480, 9234, 31845, 14927, 32683, 10787, 2687, 25372, 3756, 19744, 30019, 22542, 10730, 8452, 9727, 10673, 363, 2218, 14261, 8368, 20661, 20820, 7495, 10948, 16935, 5895, 1230, 10810, 3428, 20478, 25397, 12089, 25018, 19822, 23828, 16313, 5701, 30657, 20546, 30779, 32451, 22157, 3816, 29373, 1432, 24582, 29542, 10214, 6088, 24692, 11265, 19449, 4123, 7463, 10717, 6346, 30758, 32265, 28131, 1167, 3619, 28575, 20971, 6662, 15626, 31085, 31071, 9538, 18469, 26304, 14188, 10410, 12564, 19032, 888, 31466, 13600, 4689, 7471, 29721, 25863, 28558, 22977, 26799, 24607, 10711, 5877, 24163, 10394, 5702, 7480, 14714, 9742, 21541, 31955, 8902, 24412, 27345, 6303, 10188, 3494, 515, 1896, 25152, 27107, 31511, 25586, 32145, 24242, 6096, 6278, 21515, 21741, 7047, 3982, 27146, 23781, 556, 32516, 30060, 8244, 26911, 7618, 3587, 27375, 20429, 12005, 28640, 29593, 9485, 4251, 9294, 13347, 22541, 13399, 3686, 24679, 28125, 19414, 9395, 11228, 20269, 26748, 20660, 7166, 4348, 21594, 29943, 32422, 2959, 1638, 16573, 15053, 5203, 21914, 20999, 7198, 18726, 11362, 23181, 21055, 27119, 9392, 29132, 5392, 19636, 28222, 15418, 25837, 22508, 2291, 9569, 29187, 29159, 27855, 31526, 25099, 7974, 31407, 32424, 20294, 30476, 19622, 26135, 1599, 4450, 27357, 26852, 28256, 6408, 26516, 26696, 13876, 26050, 6650, 8451, 27898, 12211, 10299, 23059, 17646, 12436, 30129, 17, 11227, 12492, 24438, 12705, 28129, 10783, 29571, 25630, 19563, 28895, 1404, 6298, 25562, 23660, 12483, 23235, 19999, 10613, 31086, 2292, 27118, 2234, 3606, 9286, 11689, 28743, 27407, 26939, 30171, 20522, 21010, 10731, 14777, 22101, 8149, 25823, 2772, 22752, 12592, 23339, 10233, 9014, 5680, 13404, 17662, 3718, 30642, 12551, 21446, 7600, 15371, 13237, 24939, 32429, 12588, 20985, 115, 7660, 22408, 9710, 995, 7705, 8295, 12056, 7904, 13811, 22346, 20806, 19697, 25210, 30565, 6060, 17162, 16451, 4810, 26552, 26400, 22853, 30644, 4048, 22801, 22297, 17710, 2413, 25842, 27623, 17742, 24699, 26665, 24549, 11177, 10099, 18380, 8207, 5672, 13311, 21246, 4807, 28086, 31127, 25669, 9142, 5523, 14582, 9478, 3299, 19496, 12765, 30246, 21584, 120, 15879, 20816, 23299, 24158, 10005, 5557, 26246, 24838, 28031, 2430, 587, 21255, 31225, 28969, 10187, 2524, 23321, 8359, 20026, 29077, 7946, 2032, 16567, 779, 9702, 14677, 7799, 18674, 23057, 4199, 27766, 29403, 31649, 28158, 31571, 3398, 11844, 5785, 10049, 31672, 4973, 68, 28930, 11723, 22522, 21824, 29645, 18093, 11644, 23457, 2837, 22645, 5587, 11041, 27831, 3977, 9217, 28305, 15682, 31787, 7930, 17077, 32568, 24398, 9216, 23282, 28618, 12797, 8946, 26700, 13529, 8815, 14450, 2099, 21942, 29246, 20209, 27569, 6449, 29034, 24902, 23048, 28626, 5740, 17164, 30344, 22200, 30538, 24809, 6797, 31563, 15207, 32052, 10779, 24878, 24771, 12402, 9826, 20097, 359, 4023, 787, 31147, 7211, 6919, 27727, 12749, 14758, 30795, 21384, 22812, 5860, 21438, 12320, 20761, 17751, 12152, 19826, 29957, 8082, 8325, 12869, 10704, 28017, 22335, 11905, 9003, 10115, 12451, 25848, 15285, 22257, 4672, 24869, 3973, 32628, 22460, 11902, 4425, 7315, 7645, 19428, 32026, 4955, 27961, 11653, 10210, 30596, 24920, 1845, 1266, 6331, 18605, 29824, 6607, 8758, 7777, 3740, 10835, 21848, 32471, 1798, 29736, 31522, 1544, 6942, 20733, 31337, 15217, 27519, 21667, 24270, 3254, 21558, 29095, 25051, 7410, 32259, 27751, 11198, 10478, 13417, 7287, 7908, 27677, 26367, 32728, 32049, 26295, 5338, 6987, 4194, 1902, 18034, 25227, 29399, 23081, 27935, 24501, 8155, 2439, 31564, 27921, 2735, 19517, 26862, 3589, 9957, 11985, 24573, 25504, 21046, 2624, 23329, 24369, 9172, 24789, 2752, 9854, 23879, 3508, 5315, 24123, 1101, 20739, 30010, 31756, 13138, 10614, 19240, 7140, 26936, 14517, 18711, 31764, 8274, 28563, 30148, 19859, 27277, 24790, 2074, 26065, 7589, 1261, 25355, 14395, 17314, 560, 20010, 10752, 2391, 2627, 8039, 30857, 26669, 9587, 21869, 862, 25303, 15352, 17019, 741, 24822, 5052, 27925, 19838, 2765, 10269, 13057, 20148, 22339, 13094, 32264, 18433, 25920, 19374, 30606, 5898, 17806, 29038, 12349, 18452, 32299, 23824, 21690, 1172, 15752, 24316, 23039, 20108, 27823, 20839, 8944, 23997, 107, 19579, 8192, 1937, 32001, 32550, 4184, 19421, 18425, 30677, 22154, 8077, 1112, 29809, 27658, 4668, 6952, 7896, 4648, 8054, 24059, 7202, 24451, 25039, 26442, 26468, 11084, 26196, 24564, 3146, 20107, 30027, 32115, 12186, 20709, 166, 20969, 4735, 30549, 21123, 7762, 22234, 25878, 222, 12979, 5801, 10770, 19848, 11363, 10226, 8494, 29166, 2380, 12789, 21283, 410, 6165, 27652, 15962, 13158, 29302, 6314, 30688, 32441, 13176, 8961, 22198, 6043, 29982, 2504, 21656, 30663, 2553, 30283, 17829, 5213, 9134, 22656, 7352, 14985, 30197, 15992, 26970, 31298, 29247, 4918, 627, 9017, 20518, 15609, 30112, 25059, 5549, 12813, 23906, 5091, 13555, 10401, 15939, 23644, 14987, 4399, 10719, 12519, 21285, 22771, 11059, 27045, 616, 22682, 29919, 18687, 11608, 12883, 25974, 6982, 11220, 8520, 28225, 21635, 8839, 3234, 20835, 5327, 17479, 8475, 19519, 22742, 19288, 31702, 24256, 25745, 27245, 20242, 4790, 31506, 21678, 172, 3713, 25120, 21742, 13975, 19729, 13715, 3669, 7698, 11938, 6580, 3819, 19411, 8398, 14870, 5243, 9562, 18197, 1893, 13342, 31388, 31171, 15654, 13112, 9785, 2075, 29926, 18468, 7903, 4439, 11494, 15497, 10525, 24669, 20622, 6717, 27221, 11406, 12172, 3674, 7910, 25671, 26334, 660, 21692, 12823, 22956, 5336, 21226, 23579, 25718, 13530, 25708, 1100, 9700, 25465, 2386, 21451, 10181, 2783, 14750, 1856, 10939, 26999, 5129, 29251, 30798, 9867, 11695, 30643, 11866, 8099, 22430, 22738, 31399, 12651, 20991, 23066, 6785, 14951, 27647, 9777, 22947, 22714, 7395, 26030, 22434, 25073, 9539, 10959, 21140, 19934, 10325, 10878, 27654, 4814, 31338, 25961, 6567, 28796, 17258, 28058, 8471, 3069, 30940, 9704, 29376, 11821, 10709, 30383, 25958, 18574, 11046, 25165, 10950, 15268, 30496, 20768, 10714, 12613, 12700, 30153, 28039, 6899, 22618, 2296, 4473, 32285, 7856, 16709, 6555, 14452, 6893, 27231, 21779, 25354, 5826, 27050, 28175, 17283, 21329, 265, 14894, 10853, 21454, 28004, 27000, 10612, 4470, 14222, 975, 24954, 24772, 416, 8119, 23264, 21530, 21715, 12144, 32337, 28794, 23500, 4304, 30561, 1819, 13287, 26640, 23862, 6935, 10251, 23798, 29408, 28185, 3408, 22774, 23531, 18782, 6694, 8038, 20023, 19164, 32035, 14190, 8914, 575, 12951, 3613, 5467, 8512, 6109, 13348, 12850, 1504, 3599, 12596, 11043, 764, 25596, 20908, 21063, 1232, 20458, 15644, 11061, 13294, 8298, 2025, 14789, 22689, 11955, 2986, 22189, 27188, 16874, 8702, 1785, 26083, 30206, 8198, 8294, 9292, 11998, 1596, 2033, 12232, 31036, 12077, 16233, 23018, 20497, 2374, 1746, 31467, 12615, 11580, 15754, 5611, 20007, 21242, 11885, 25683, 4140, 8906, 217, 1451, 4989, 13513, 26877, 18868, 24601, 28095, 15723, 8571, 20499, 14114, 271, 186, 22029, 18307, 32409, 20467, 16872, 19619, 24950, 25827, 29106, 26406, 9386, 6285, 29748, 20155, 12744, 20365, 9979, 11641, 13104, 6255, 5444, 10763, 24288, 9735, 31750, 22023, 22398, 11734, 6294, 31106, 6223, 4606, 28012, 2813, 11802, 9255, 11680, 22022, 23097, 7197, 19065, 30719, 8409, 16348, 1904, 18544, 25582, 14585, 16262, 6961, 22293, 21173, 10462, 12609, 10109, 25138, 28403, 15521, 1589, 9819, 25838, 21812, 9020, 8892, 6348, 21381, 30903, 21819, 21094, 11974, 11002, 24545, 8287, 15786, 5218, 992, 7207, 6653, 31596, 11432, 25040, 17799, 18204, 24841, 24907, 11401, 4519, 25865, 3899, 24172, 11396, 7709, 31392, 32249, 30607, 2013, 6679, 12227, 16049, 32545, 24145, 1874, 10583, 7014, 8097, 30005, 1388, 30869, 31017, 6143, 13575, 30506, 7029, 11995, 13115, 612, 14290, 20080, 4465, 2089, 4804, 23190, 1464, 15044, 27648, 19820, 13148, 19705, 7233, 8572, 31876, 13752, 9311, 22743, 20303, 7105, 27777, 20250, 32176, 21177, 8854, 31805, 16564, 23662, 30004, 14571, 14802, 29370, 11175, 20506, 10079, 15806, 18172, 8438, 31309, 31245, 31721, 9150, 13920, 23696, 10710, 5111, 29378, 9312, 7734, 23711, 12470, 31580, 12715, 13482, 12225, 31028, 14365, 9280, 5975, 20752, 3272, 3702, 12308, 19893, 31385, 4613, 14137, 11770, 9956, 9554, 9918, 18799, 10399, 9073, 10736, 25380, 15226, 9954, 18079, 160, 14040, 26647, 8766, 13036, 1133, 23246, 19528, 26448, 32599, 11917, 10781, 9585, 7820, 3748, 17310, 11119, 27667, 12868, 27745, 25202, 15130, 15218, 29433, 22700, 10977, 21171, 26575, 22382, 9241, 26954, 12373, 14482, 11207, 31121, 19242, 6476, 11033, 16721, 4080, 21191, 29980, 5247, 9434, 8208, 22660, 32488, 32680, 23311, 8050, 28511, 13570, 12020, 12751, 4342, 13218, 19776, 15580, 6154, 19284, 4476, 17560, 1277, 23291, 9745, 5101, 17446, 19609, 28437, 31902, 11990, 15201, 25555, 3284, 19027, 8488, 27646, 13861, 8655, 4544, 9817, 13109, 30402, 23302, 5617, 31800, 21820, 32277, 24926, 2778, 14211, 3385, 5490, 28141, 22160, 5727, 7894, 10801, 10435, 4696, 22635, 6277, 27328, 13805, 23197, 6873, 24291, 12847, 30548, 27564, 5595, 12822, 5748, 31812, 274, 18101, 8903, 25688, 14897, 24585, 31997, 6853, 11093, 12587, 9283, 12539, 15532, 27755, 3467, 928, 18049, 26421, 26354, 29119, 8717, 26771, 12143, 28417, 24747, 29783, 7840, 28485, 11804, 19361, 18841, 32760, 9427, 9195, 30151, 31378, 3371, 16123, 12285, 31546, 5673, 1553, 4215, 12653, 12496, 29406, 15711, 179, 3329, 1037, 12718, 980, 30766, 9976, 7439, 18371, 29424, 13062, 19936, 2552, 5330, 24098, 31272, 19059, 31559, 19873, 22349, 1483, 20471, 28109, 21012, 30326, 28548, 32234, 564, 6114, 23443, 19650, 30089, 8487, 1305, 9181, 28407, 20384, 10314, 11975, 12364, 21031, 12255, 31695, 10125, 8307, 17243, 26086, 12106, 1998, 13067, 10829, 23423, 2197, 20635, 30073, 24786, 20402, 29772, 26275, 17060, 5501, 29935, 11746, 13565, 3347, 9271, 29274, 17837, 3600, 12103, 20287, 959, 28660, 15659, 19336, 23249, 22598, 21798, 24923, 9849, 6492, 12199, 22231, 27029, 1629, 9174, 22996, 13098, 27660, 1300, 21751, 11278, 15307, 2149, 28227, 4559, 13056, 21896, 7218, 27971, 18209, 8332, 9792, 8883, 12560, 10022, 11079, 20960, 31904, 27207, 6828, 4204, 1784, 8446, 2763, 26795, 18475, 23690, 969, 21652, 9381, 31364, 1624, 28949, 9938, 4937, 1761, 13409, 4864, 478, 19596, 12958, 25675, 1508, 16186, 12997, 27562, 29516, 11017, 23620, 8500, 28483, 7934, 20719, 16150, 9638, 8141, 7967, 19800, 29538, 1762, 30243, 7232, 28140, 15999, 16370, 3322, 23236, 19890, 27905, 11639, 4228, 12929, 25075, 7040, 1302, 23697, 1020, 16851, 10728, 13723, 25453, 17555, 10328, 16039, 16676, 29144, 31847, 14858, 10207, 19089, 17766, 25730, 27496, 14370, 17215, 28672, 3963, 2219, 12981, 257, 10069, 5200, 22968, 5834, 3015, 26055, 31475, 12528, 23689, 19961, 29742, 18199, 7529, 19536, 27592, 23571, 9184, 19386, 9560, 9408, 2975, 26938, 12404, 11742, 21076, 28228, 29502, 1311, 2366, 31082, 24211, 2744, 6601, 22949, 10236, 12514, 535, 24331, 3918, 20684, 9509, 2897, 19261, 29410, 19892, 13425, 8321, 108, 7628, 13198, 18883, 4819, 1839, 5270, 29380, 5776, 32496, 21786, 12579, 25507, 21442, 7752, 19547, 26189, 21941, 7342, 22753, 8296, 18514, 8067, 2780, 24868, 20555, 27678, 12447, 23581, 18744, 17856, 31662, 30962, 24035, 25258, 9339, 17840, 2424, 21629, 11036, 22256, 3209, 21720, 18821, 28906, 14036, 6246, 13528, 27327, 9005, 5645, 14053, 28603, 29803, 19322, 334, 20046, 18701, 32552, 9582, 21796, 9839, 29052, 29411, 20495, 19309, 6405, 26016, 31718, 14249, 10874, 9994, 12748, 11178, 9703, 30672, 11901, 7671, 25346, 22240, 17338, 9575, 12271, 2999, 23416, 8478, 3384, 30217, 31739, 5205, 23173, 23347, 8089, 12029, 26613, 4584, 1513, 30859, 11981, 13350, 6433, 23543, 7681, 11636, 7061, 23510, 25383, 3303, 2485, 595, 10848, 25913, 5734, 7963, 29137, 26846, 27542, 18742, 15426, 29794, 6769, 24630, 18535, 31228, 8422, 10458, 12298, 3615, 1823, 30439, 23549, 13794, 22518, 11252, 7936, 8736, 22004, 13418, 18419, 21040, 3203, 50, 12752, 21490, 4111, 27400, 3970, 17339, 10348, 31479, 18594, 3574, 7015, 18224, 8931, 23651, 32331, 23910, 23809, 13857, 13034, 29068, 12051, 32388, 27090, 31446, 21628, 18973, 1067, 5655, 29610, 28144, 2797, 21663, 12161, 4976, 8516, 13586, 20624, 23815, 4226, 20665, 10601, 3828, 29530, 28360, 22397, 18638, 30717, 29984, 8773, 6436, 23242, 7594, 21266, 2970, 749, 23839, 15516, 30348, 23822, 25585, 10347, 15856, 15995, 4119, 31675, 26502, 6158, 15021, 6638, 22235, 20629, 30924, 23703, 14076, 32761, 13216, 30679, 17868, 7219, 10343, 30337, 2561, 15585, 25752, 13719, 11793, 28401, 7307, 8201, 22673, 22734, 19196, 602, 20812, 26226, 23793, 3076, 215, 21218, 28905, 24394, 31052, 19525, 19699, 29364, 10265, 20088, 10800, 28096, 12468, 11348, 12109, 7845, 1917, 15965, 27266, 5457, 815, 29319, 24831, 8013, 32401, 1605, 1208, 16025, 18750, 26759, 10534, 13225, 23599, 31129, 24267, 13938, 14011, 24635, 32198, 12350, 31620, 13140, 561, 18789, 10628, 1628, 12153, 7884, 21876, 24293, 2465, 31241, 29529, 9329, 16106, 32200, 17254, 20980, 29963, 13077, 20766, 30600, 32565, 18587, 19575, 19425, 25797, 16604, 12426, 28068, 6453, 7964, 27919, 21977, 26691, 20146, 12419, 29840, 5124, 4056, 26931, 10513, 21771, 7477, 7979, 6556, 6709, 12834, 24088, 14014, 5195, 4533, 21468, 17218, 31607, 29802, 27033, 16373, 10536, 3151, 19012, 10625, 11412, 18076, 28235, 29889, 14911, 13891, 32395, 20048, 6981, 11922, 25148, 26046, 1235, 1069, 28301, 24687, 6409, 11266, 19708, 3647, 9673, 12385, 18331, 19701, 2348, 4058, 1826, 13327, 26553, 21362, 24990, 18015, 6743, 14032, 13858, 19817, 11976, 15468, 1760, 26165, 10356, 18526, 5636, 4520, 13766, 1738, 4740, 3738, 20105, 3993, 7662, 30081, 23170, 24434, 8548, 30025, 6462, 22433, 26866, 29493, 11897, 30904, 22899, 28060, 4822, 294, 20210, 20239, 24670, 18777, 19303, 16229, 18511, 1867, 28261, 23778, 18901, 2453, 20822, 28770, 5930, 22606, 8444, 8974, 3608, 14718, 27604, 6494, 18010, 23863, 27748, 3944, 22958, 12283, 10703, 12839, 9250, 10216, 9861, 21338, 30989, 18922, 7168, 24713, 11934, 7995, 25910, 9378, 18273, 19694, 9807, 13429, 2319, 1062, 1053, 19532, 30414, 29024, 14228, 21096, 10056, 10509, 27035, 1659, 13016, 27807, 3697, 12178, 7866, 27690, 23143, 21125, 10804, 1541, 10791, 4708, 11032, 32014, 14169, 19193, 9229, 23350, 11071, 18295, 7844, 22963, 18495, 31178, 18967, 31368, 31237, 29735, 31035, 22003, 1722, 20559, 1729, 15303, 7643, 27911, 18924, 13836, 12952, 19732, 5474, 30786, 21352, 13866, 21296, 21955, 5194, 16614, 5858, 20549, 3392, 21165, 21149, 20196, 15798, 13671, 11864, 23916, 28107, 18713, 4219, 24402, 26377, 1651, 16004, 11893, 29194, 9410, 5847, 2311, 18507, 27293, 28126, 14195, 10154, 3862, 9600, 7788, 6117, 25169, 14487, 14620, 3484, 20887, 10695, 1403, 20141, 8776, 24110, 11303, 2001, 20162, 12080, 24819, 24768, 14283, 18885, 22193, 22069, 23345, 11244, 11389, 20390, 12487, 24906, 13988, 29723, 22217, 9885, 9185, 29852, 18069, 30624, 11752, 31642, 30499, 29041, 13845, 14587, 29511, 21981, 28327, 16642, 21936, 26839, 12943, 21261, 11288, 6589, 14510, 5395, 24586, 30178, 26245, 10108, 8423, 30905, 13741, 30551, 21331, 19320, 4531, 19843, 14451, 12477, 8209, 24286, 24872, 22948, 22876, 2353, 15767, 15332, 24297, 6701, 2155, 29476, 4694, 12703, 14456, 14567, 23490, 9349, 2022, 10434, 21971, 19513, 25660, 19901, 13860, 20788, 22184, 8691, 18837, 13775, 6626, 30587, 2666, 17380, 21403, 1666, 9136, 9259, 17419, 17509, 29817, 14528, 27497, 5341, 8342, 2362, 3868, 1672, 14787, 10136, 4984, 3116, 12240, 5025, 24051, 1486, 19064, 19941, 5376, 11179, 18658, 32074, 22163, 10989, 7834, 5165, 12764, 11449, 9230, 20240, 25132, 548, 1297, 12252, 3893, 28646, 11673, 5870, 26179, 26990, 272, 22984, 11657, 4370, 26240, 29856, 2236, 8728, 13546, 24202, 1536, 31584, 2933, 19142, 23352, 27599, 22906, 24328, 27696, 8249, 9070, 18342, 18601, 12692, 16914, 16429, 11339, 7948, 23414, 25767, 6474, 8152, 25474, 27491, 18072, 22886, 29092, 31960, 8539, 21721, 18344, 133, 31996, 29474, 1708, 7891, 3920, 13006, 6517, 11488, 11380, 29460, 3986, 26683, 23491, 21175, 15870, 19442, 21699, 12679, 32526, 31092, 9798, 21213, 8765, 16523, 23584, 22176, 24225, 1185, 28493, 8769, 10603, 23285, 32219, 11289, 29525, 14766, 8402, 9106, 1019, 31714, 11108, 23408, 26913, 17228, 7336, 25019, 8895, 13255, 2056, 7333, 180, 11536, 3678, 19317, 25098, 25243, 8016, 18067, 27812, 31152, 31436, 20846, 30123, 20313, 22030, 26045, 8960, 25366, 29472, 28121, 8206, 14042, 29820, 31221, 492, 8998, 31774, 15935, 15932, 13699, 11100, 8236, 20154, 28602, 27882, 30829, 23233, 23065, 14347, 10470, 14905, 10492, 28797, 2566, 23740, 27014, 3159, 27485, 7244, 4897, 30074, 29045, 6944, 18852, 499, 21602, 12990, 5054, 21761, 24864, 605, 26783, 11831, 30395, 1223, 5061, 23401, 1831, 12699, 32442, 23924, 2642, 16577, 23474, 24344, 1551, 8104, 13978, 11618, 8625, 11142, 18225, 15474, 6780, 6116, 28953, 23530, 14794, 26185, 13291, 17926, 27556, 18073, 27509, 18816, 9018, 31211, 22075, 8331, 30360, 32715, 17391, 21932, 4330, 32318, 29489, 10333, 4825, 8499, 14269, 21334, 8789, 12663, 2843, 18504, 3033, 5955, 27518, 32511, 3451, 25413, 3355, 9210, 17599, 43, 1485, 28787, 785, 28687, 11442, 17813, 19733, 12753, 12497, 21402, 2198, 32199, 11578, 23037, 5941, 20459, 32291, 26454, 4624, 17010, 30, 29595, 12405, 18520, 19993, 14391, 4405, 1249, 14101, 26234, 27353, 26873, 13960, 28571, 6642, 19857, 31366, 26150, 28818, 7877, 11780, 93, 28341, 15479, 16069, 6099, 29030, 29390, 31047, 26125, 28173, 14068, 14470, 12333, 18665, 20399, 4904, 4463, 14727, 31547, 7587, 30322, 30335, 13926, 8887, 3049, 23625, 894, 30076, 4991, 22311, 13280, 31160, 30134, 26357, 3077, 26387, 24009, 12120, 28471, 18332, 32172, 19756, 24309, 17299, 28316, 16503, 18656, 27926, 993, 22202, 1558, 25711, 6570, 23428, 24177, 21189, 27716, 6098, 3265, 10780, 4037, 21919, 30321, 176, 6243, 27220, 30888, 8584, 15073, 13639, 6011, 20983, 31273, 10278, 12310, 31579, 11810, 14357, 11489, 27897, 11485, 7139, 2571, 11984, 26105, 18825, 4952, 29665, 16368, 12002, 3999, 14882, 6316, 32443, 23985, 12601, 9971, 3315, 21655, 6735, 7638, 10856, 25448, 7146, 9461, 13385, 13527, 14411, 5817, 21020, 13017, 19219, 10379, 24952, 25527, 25213, 12429, 27319, 22435, 17261, 15209, 19249, 26750, 30975, 9024, 27373, 6600, 9779, 19181, 1989, 18755, 15974, 8947, 26341, 25320, 28229, 19031, 20791, 12509, 18578, 5102, 219, 13532, 25687, 31097, 4073, 28547, 14511, 25778, 2581, 24219, 13339, 24121, 28052, 30014, 19404, 15104, 29553, 6791, 11328, 150, 2955, 22038, 13203, 13324, 22835, 5262, 23499, 23070, 9284, 16431, 22562, 8443, 25717, 12241, 8876, 6199, 13820, 32012, 19364, 19110, 15126, 21303, 4005, 8210, 30927, 18543, 32236, 32114, 21211, 12331, 1140, 7403, 18216, 12398, 32080, 20230, 2954, 18372, 15129, 21057, 19189, 5608, 20532, 25407, 18286, 22259, 876, 8534, 18988, 7165, 9337, 1622, 10537, 23421, 9233, 6033, 25989, 11310, 19349, 13180, 20740, 29278, 19099, 25844, 19568, 30629, 2076, 3334, 18002, 4811, 9108, 17952, 27707, 10220, 3717, 6093, 24506, 14384, 16316, 20378, 9236, 26853, 26493, 20614, 19583, 32445, 26585, 14349, 22719, 25796, 14111, 29632, 24516, 28161, 3750, 24180, 18193, 23418, 7005, 482, 17649, 31776, 26309, 13918, 7556, 19439, 2580, 31790, 14906, 12128, 23280, 6362, 25732, 2390, 9332, 31633, 11201, 15298, 15930, 11948, 5201, 25324, 10631, 3097, 28625, 8078, 2867, 3479, 3791, 13471, 8908, 18241, 6644, 15688, 19559, 21280, 23238, 17834, 30013, 14139, 9203, 824, 16745, 13344, 29209, 7724, 20738, 31170, 20587, 18144, 26838, 14521, 22118, 13737, 19073, 7115, 23721, 12726, 10863, 15709, 7723, 15440, 22000, 13887, 6049, 11107, 30161, 16513, 1894, 31908, 28358, 25488, 10296, 6271, 3414, 6905, 13834, 23304, 6863, 10967, 12831, 17141, 5198, 2874, 27715, 29917, 2597, 23422, 23435, 14230, 29505, 29418, 3067, 9940, 13633, 18141, 13185, 25289, 29651, 22540, 10588, 17387, 13516, 7354, 9244, 17907, 18790, 476, 31499, 28133, 30304, 6029, 17600, 26013, 11527, 24383, 5888, 14484, 14926, 23451, 25292, 10050, 1449, 8084, 10576, 916, 17548, 23560, 21311, 21554, 31317, 2247, 4065, 577, 25192, 30249, 9383, 19621, 30913, 11482, 24944, 2667, 1376, 28710, 27449, 28280, 24483, 26728, 319, 13828, 951, 11598, 6261, 29186, 9476, 27003, 12112, 30431, 16881, 30299, 9147, 27913, 17863, 14634, 28843, 8877, 19461, 1621, 19010, 26606, 25699, 7345, 8771, 8621, 7034, 30928, 27702, 23027, 28885, 12395, 25802, 14661, 12367, 19585, 8746, 12908, 30841, 28506, 1141, 19498, 30407, 19305, 23557, 13660, 13243, 19211, 6965, 9629, 926, 27820, 11219, 7989, 28576, 9157, 18105, 28114, 977, 28613, 31557, 4130, 8330, 12334, 22331, 32684, 26739, 14433, 29285, 20232, 15577, 3047, 3551, 15684, 27731, 13939, 18142, 27922, 27868, 1083, 3269, 3962, 17695, 25234, 18112, 6551, 796, 19244, 9696, 4423, 23604, 23367, 25044, 11747, 25758, 32416, 2586, 15291, 12775, 31513, 22718, 9347, 4335, 7459, 20974, 2536, 26071, 3520, 4884, 32245, 13693, 30491, 26035, 20904, 22631, 26059, 17511, 13026, 25817, 25323, 590, 10480, 4601, 21254, 27987, 7973, 4268, 10131, 9682, 31202, 25617, 4122, 26874, 12219, 3755, 13024, 30729, 19120, 23539, 32147, 9944, 4800, 20153, 7311, 9303, 25426, 5825, 15156, 21923, 19121, 12134, 7892, 14189, 5688, 6633, 4839, 12085, 24683, 22605, 30705, 11813, 20774, 591, 7512, 12228, 14301, 23255, 19233, 16133, 9639, 18089, 12630, 22613, 11387, 6861, 17614, 29654, 384, 19986, 27117, 18091, 25978, 26716, 752, 25943, 28234, 32160, 20044, 13899, 30998, 4352, 18004, 19849, 22369, 17329, 27412, 25196, 9641, 25370, 11515, 13847, 17396, 19992, 4046, 5766, 7308, 28383, 11109, 12643, 770, 31749, 23376, 6497, 11601, 31782, 8657, 3237, 22455, 17423, 27675, 17820, 25965, 17747, 22377, 18652, 9648, 24207, 20591, 4574, 15627, 32119, 4660, 6790, 6812, 15508, 4272, 277, 20938, 15968, 19856, 18430, 27130, 21483, 24364, 12731, 29312, 19049, 22046, 28674, 24070, 24342, 17769, 13767, 19889, 20720, 28250, 9226, 13576, 7680, 30336, 6623, 23678, 5046, 9123, 15161, 9398, 5518, 20662, 9545, 13182, 27004, 19153, 31074, 10973, 8297, 24508, 11548, 4298, 9382, 27670, 22975, 22637, 13802, 9701, 5017, 21131, 8233, 903, 13334, 2835, 3996, 25554, 30083, 17450, 19614, 30204, 28766, 29214, 14397, 25121, 31743, 16585, 19669, 27295, 14260, 28108, 24528, 4275, 20468, 16613, 15555, 24226, 1022, 13588, 14371, 20569, 22268, 26100, 950, 6128, 23590, 22748, 8786, 22412, 11257, 8191, 7022, 29260, 26830, 11573, 11677, 17656, 29880, 29413, 13591, 22094, 15030, 4779, 14786, 9322, 18874, 14764, 9414, 23295, 31687, 19809, 12636, 28671, 22208, 29705, 26170, 23460, 31970, 23320, 15392, 25195, 1415, 12430, 13945, 26131, 26211, 22783, 22745, 3693, 10454, 15681, 12991, 5929, 26440, 23843, 5067, 15838, 28165, 11507, 21999, 15797, 20125, 18185, 28729, 14980, 30017, 10633, 14359, 16286, 7655, 14628, 1303, 28414, 9245, 11633, 26682, 29324, 29708, 9760, 23952, 31600, 8464, 27802, 10046, 23384, 30331, 9822, 29725, 11940, 11368, 8481, 28974, 24619, 28377, 26805, 7704, 24704, 10926, 12664, 4603, 15252, 15436, 25805, 2614, 11523, 11470, 12095, 27962, 29277, 29275, 4587, 629, 19637, 17783, 12184, 31247, 32407, 32660, 5452, 10369, 32690, 1634, 1918, 14626, 26955, 26614, 26674, 29598, 14033, 20422, 7994, 22269, 28209, 32493, 7673, 17136, 9267, 20953, 14656, 17754, 27642, 6983, 21752, 26251, 3089, 11923, 29584, 10290, 17147, 4430, 7077, 5309, 28120, 2171, 14931, 12380, 17133, 20200, 1011, 19400, 17698, 19572, 16569, 14845, 24249, 30456, 20544, 26776, 29446, 22676, 14221, 14166, 15583, 9121, 17587, 19825, 18351, 20681, 9597, 9371, 24623, 8008, 3173, 27613, 31042, 15919, 11559, 26612, 25999, 28627, 29650, 19886, 19599, 7767, 2012, 21166, 23738, 13672, 14417, 16505, 20120, 7064, 29662, 19760, 12777, 2479, 5316, 28147, 24411, 19816, 393, 21607, 2803, 10248, 9665, 9293, 29894, 11562, 28731, 12370, 32559, 19741, 19014, 1498, 19267, 14680, 31077, 364, 32675, 11879, 16003, 22182, 16272, 7742, 18892, 25800, 7907, 30011, 5186, 8474, 5988, 743, 26731, 12518, 27535, 27949, 1747, 12195, 25475, 21421, 25724, 7635, 24077, 14479, 25408, 25962, 2991, 14378, 31966, 8661, 7893, 24341, 9773, 665, 12947, 26430, 13144, 24166, 11355, 25218, 21059, 25208, 27396, 6989, 24559, 10364, 1096, 20104, 14321, 26283, 11236, 12683, 22442, 14863, 19493, 18894, 30968, 7096, 20871, 17170, 26315, 28450, 31246, 20511, 28963, 28753, 4671, 14156, 29416, 6571, 2169, 14690, 21092, 4767, 21544, 26202, 8800, 21317, 9933, 7566, 31588, 16860, 14322, 31486, 10097, 2902, 12812, 8798, 24738, 8218, 3521, 14465, 11758, 14961, 2764, 19247, 20085, 24419, 30808, 2116, 32719, 20621, 18555, 9865, 3847, 411, 10272, 6299, 9660, 17250, 31913, 27838, 1397, 16539, 9592, 17707, 9691, 32269, 879, 13618, 1385, 6908, 18302, 16939, 2784, 15446, 11750, 18562, 24001, 3922, 4440, 30617, 8009, 21774, 14666, 7156, 24324, 1580, 6192, 22871, 1209, 30756, 16795, 27600, 20751, 28387, 10366, 31587, 30651, 23153, 4390, 16661, 29386, 8135, 23178, 98, 11040, 13313, 9974, 18774, 3764, 1649, 19765, 31105, 790, 17316, 15949, 3140, 536, 17365, 4257, 20246, 20870, 1475, 27386, 23617, 28198, 27262, 15124, 360, 14126, 22736, 13566, 1676, 4741, 18267, 13948, 7150, 16192, 13683, 4064, 8196, 16574, 26789, 17435, 23106, 20568, 29792, 13102, 7272, 29903, 29883, 14993, 19086, 10398, 26498, 17652, 30144, 18285, 15325, 22595, 5751, 25463, 14579, 16730, 18439, 19092, 17225, 1748, 11712, 6211, 11709, 3088, 27475, 25325, 13223, 28925, 10913, 32369, 1032, 6342, 20578, 22768, 26707, 20020, 27257, 24138, 23496, 7295, 19016, 26479, 12681, 15034, 17880, 4158, 29057, 2551, 10126, 26926, 27510, 12439, 24888, 30628, 22986, 16462, 26326, 16662, 31448, 25850, 29946, 3653, 21785, 18704, 27116, 18860, 23250, 29270, 14312, 27183, 18623, 15088, 25625, 14499, 24238, 17896, 654, 23145, 30612, 19223, 24756, 13626, 18936, 19128, 8999, 30441, 2793, 23808, 25168, 13131, 6739, 30318, 20862, 9269, 7924, 5287, 27398, 28218, 27100, 24664, 14936, 25008, 29716, 10476, 6068, 23021, 18865, 12995, 13923, 10594, 4043, 31307, 22802, 8293, 20038, 10550, 15913, 4022, 18223, 28981, 32023, 7775, 9712, 1597, 14793, 16963, 1078, 948, 11954, 11145, 23020, 25181, 22724, 13700, 24911, 13479, 8041, 13670, 9824, 6480, 5130, 26848, 7745, 24792, 30486, 14942, 30143, 11484, 19407, 16221, 11716, 26548, 16777, 30748, 20437, 31470, 28725, 24672, 12772, 5269, 23104, 689, 30778, 11292, 28463, 30250, 17337, 12713, 22936, 24928, 31700, 19133, 3138, 14617, 5164, 26404, 19258, 24517, 8340, 13554, 26910, 18454, 10119, 29211, 14358, 29546, 8609, 8139, 2873, 26151, 19518, 31612, 26870, 28543, 29583, 26047, 32230, 25511, 22581, 14109, 29710, 31982, 9456, 31697, 30261, 7255, 19492, 4618, 12366, 29284, 24802, 1509, 16821, 14224, 14478, 12945, 3074, 32709, 12167, 8276, 29368, 31224, 16842, 14699, 16971, 21182, 9743, 24854, 12893, 17202, 16783, 32335, 9263, 27218, 21568, 16120, 25393, 23383, 23230, 2527, 7493, 11150, 28612, 10418, 8093, 14352, 6015, 8448, 9848, 10908, 16353, 14561, 18802, 1512, 12909, 23962, 21410, 13493, 28989, 16926, 30514, 22516, 15085, 2904, 18577, 12410, 7508, 24801, 30787, 28314, 3096, 2766, 11565, 16329, 27405, 10850, 12669, 7348, 3464, 15092, 32124, 26012, 26899, 2017, 24189, 1502, 16896, 25983, 9037, 25995, 10493, 27115, 20014, 29930, 17208, 20696, 21821, 11015, 32317, 23734, 10071, 6442, 11524, 22551, 17736, 5016, 13219, 20277, 22364, 15204, 15016, 31846, 5317, 16816, 31804, 11964, 19011, 24159, 31423, 15184, 23614, 32153, 4250, 747, 24665, 5598, 14468, 21838, 16478, 29496, 5419, 28736, 21305, 32087, 25742, 15334, 14317, 13300, 21436, 3623, 20393, 27550, 4014, 27305, 19157, 17596, 14533, 23051, 25702, 19230, 27920, 28515, 18323, 21784, 1875, 29864, 13187, 14227, 16055, 18030, 9104, 13081, 6759, 20705, 7226, 6403, 13819, 246, 27281, 22381, 18597, 30467, 31227, 17749, 27544, 5885, 5914, 3658, 16898, 7838, 20597, 22456, 28632, 22183, 20225, 14143, 23375, 545, 1474, 21356, 11834, 28259, 32129, 11629, 17506, 13356, 15133, 17003, 16450, 27993, 7622, 12297, 16154, 22959, 25642, 18118, 18325, 16864, 17109, 24006, 21158, 27888, 19510, 25768, 312, 4207, 25840, 27871, 26483, 273, 187, 6614, 19576, 19960, 22661, 4223, 14698, 24975, 27861, 30342, 13284, 32162, 4191, 4754, 21309, 3091, 2945, 21937, 9192, 28876, 729, 6279, 24039, 9960, 19925, 13071, 26597, 6364, 7771, 6827, 19155, 10907, 6182, 17561, 30046, 17845, 15475, 19747, 25251, 14136, 5719, 17743, 27549, 3875, 10569, 3578, 5656, 20076, 2626, 22687, 24924, 9552, 27573, 3446, 10192, 18890, 6999, 13272, 24357, 5678, 28750, 23109, 4996, 9808, 16050, 2200, 29192, 23631, 20992, 21526, 8112, 14746, 4627, 18156, 1087, 19955, 12482, 8105, 15662, 13863, 9447, 14657, 31873, 29494, 21471, 8392, 22040, 8564, 29911, 16927, 20362, 32328, 5065, 4045, 28073, 7533, 3691, 25069, 2821, 27122, 24338, 23226, 19338, 8589, 10511, 14219, 2107, 23537, 10092, 10503, 20211, 28101, 14734, 9126, 13382, 17444, 27503, 15683, 25392, 19005, 11708, 24233, 31785, 17477, 3974, 15593, 15702, 30086, 20787, 30460, 15069, 7457, 28957, 24367, 13177, 10847, 17428, 10286, 15884, 279, 31143, 12566, 25091, 32244, 20567, 1878, 13669, 21540, 18417, 19068, 24097, 29165, 18168, 12906, 32282, 80, 30236, 8017, 13801, 857, 27980, 28919, 6215, 1755, 9477, 11122, 17186, 7186, 11140, 25725, 19403, 5952, 23050, 7281, 22766, 2711, 12387, 21601, 16555, 21688, 3306, 9810, 23121, 21592, 7399, 27312, 21030, 19268, 18471, 17036, 15522, 18639, 31752, 18393, 710, 3743, 769, 10074, 6918, 15578, 13710, 340, 15520, 15564, 20779, 22537, 20033, 26757, 28139, 22696, 30443, 18735, 15820, 152, 29268, 27254, 18730, 5735, 14223, 4834, 13992, 20860, 31204, 19335, 9920, 19343, 9530, 19916, 29196, 4823, 425, 6240, 5623, 16083, 15219, 30497, 10912, 1392, 17871, 3790, 5173, 26249, 24741, 19306, 25096, 12390, 29766, 26405, 26702, 26301, 23860, 10732, 16805, 24080, 4532, 14048, 3895, 14434, 27342, 1632, 18432, 24985, 15510, 94, 835, 17448, 6181, 21137, 21943, 19612, 18194, 24032, 16380, 14158, 287, 5944, 19988, 7142, 11740, 13895, 21546, 13990, 14944, 15829, 7577, 30282, 30028, 14843, 22130, 4146, 30555, 24136, 22387, 31290, 17212, 23468, 23877, 4929, 21134, 24700, 3103, 14681, 12622, 5375, 20875, 28354, 14981, 4467, 30899, 20548, 20658, 9733, 819, 19346, 9871, 22330, 23594, 5037, 21806, 20874, 15375, 25977, 991, 11682, 20596, 8924, 25905, 29804, 28920, 25672, 12574, 20520, 17551, 21532, 18395, 11557, 409, 5588, 2675, 9900, 17114, 20644, 15954, 21312, 31184, 4691, 13141, 194, 13620, 27150, 19561, 5425, 20726, 13230, 28056, 16520, 27737, 16536, 16669, 2418, 1607, 19727, 445, 15598, 20266, 7120, 15143, 29280, 29300, 17517, 23663, 6058, 20060, 12314, 8300, 20856, 30452, 31658, 22317, 10953, 14275, 24089, 16482, 1821, 2671, 17488, 32125, 12342, 30882, 28926, 28903, 27736, 24134, 4171, 20121, 17311, 6901, 11045, 9634, 30440, 12857, 17485, 16719, 18080, 24980, 14414, 3277, 18953, 28830, 31616, 20813, 5364, 15263, 20901, 10304, 16793, 8929, 8074, 19837, 16452, 17758, 28390, 13520, 5072, 21949, 17287, 20034, 9862, 21729, 20218, 13804, 12010, 29371, 22553, 31915, 16773, 24231, 19327, 32237, 30531, 677, 2729, 26449, 32595, 16960, 18624, 11686, 26409, 12861, 29231, 9823, 16832, 30534, 17957, 15321, 18320, 3341, 25147, 28565, 19534, 28821, 16041, 3701, 8537, 14234, 7632, 11371, 19813, 24551, 1322, 7313, 15687, 5404, 11525, 15645, 2031, 18163, 16458, 26864, 2196, 32762, 8966, 21565, 12247, 31325, 15206, 1429, 6378, 14233, 24385, 28195, 8689, 27636, 28615, 20560, 13461, 25175, 4792, 1129, 14829, 26212, 25772, 18077, 11839, 4378, 28836, 7978, 12558, 15445, 338, 30222, 6792, 7187, 26616, 20371, 19307, 30262, 392, 31311, 9666, 24899, 25458, 4314, 26408, 21950, 252, 28016, 3073, 32406, 1478, 26413, 8730, 539, 31951, 16449, 6886, 21647, 24893, 22669, 20914, 18234, 12741, 17779, 19363, 20775, 30415, 15915, 25240, 16651, 24251, 26056, 283, 13001, 14406, 8899, 16723, 23513, 28599, 29712, 31666, 21077, 31326, 6957, 32079, 4831, 20981, 31430, 17497, 11800, 308, 23799, 32154, 28007, 15656, 7381, 20054, 23758, 9065, 12352, 7876, 20234, 22049, 18346, 16170, 1895, 31796, 5744, 11522, 2889, 30603, 30931, 7687, 19524, 6669, 15195, 15160, 9069, 21618, 26817, 6086, 23970, 26070, 18485, 22209, 586, 946, 25427, 688, 3194, 12621, 1295, 12590, 27640, 28845, 7041, 18317, 21429, 22228, 200, 24965, 12736, 22340, 29701, 11761, 22641, 14997, 5791, 19214, 21368, 25140, 27723, 8143, 8479, 14454, 4881, 19667, 25417, 5966, 27355, 8375, 15093, 4270, 10404, 8673, 11587, 30012, 24093, 29267, 21832, 30500, 20360, 9718, 6752, 25127, 28008, 31890, 13568, 5703, 12829, 13002, 30895, 12819, 7250, 5135, 12269, 17085, 22896, 16091, 30851, 1240, 12960, 24859, 23526, 26432, 7243, 30398, 1021, 3841, 14832, 18754, 6014, 24060, 1889, 14009, 7823, 26927, 31206, 19227, 8806, 25980, 8317, 19030, 22979, 490, 26322, 32537, 191, 4758, 18226, 5698, 28584, 31159, 11815, 25180, 6039, 17556, 904, 1250, 14543, 23348, 24794, 2086, 20379, 1791, 11077, 102, 21533, 10564, 3888, 19431, 1590, 30471, 28798, 3307, 27514, 10271, 10242, 9724, 1001, 25537, 4307, 30126, 25963, 1739, 24579, 31140, 29675, 23480, 1830, 16716, 16459, 12616, 16672, 15738, 11035, 20398, 30097, 12174, 28699, 29282, 3150, 8267, 19389, 20345, 3214, 26734, 7302, 14751, 32034, 30868, 15197, 12169, 4052, 14811, 27291, 1301, 8265, 286, 23580, 3462, 9985, 25525, 13364, 4083, 7032, 6775, 5564, 6991, 18646, 22446, 8656, 18109, 2528, 26132, 18641, 32083, 24941, 13282, 29674, 32099, 12006, 22967, 20612, 95, 18767, 32321, 10064, 25984, 23459, 1043, 18602, 12485, 26140, 22188, 27722, 29915, 18188, 21500, 8436, 27287, 22375, 5923, 4105, 25814, 74, 17764, 7341, 7275, 28790, 28730, 31575, 24590, 3046, 13638, 13332, 28738, 26353, 5710, 19686, 19009, 18524, 6216, 2756, 28241, 29190, 12377, 22115, 26081, 26085, 6947, 8568, 3378, 21815, 1057, 12971, 22467, 24936, 30881, 21150, 26027, 3289, 19145, 22926, 27738, 20018, 17174, 8030, 24636, 14860, 9661, 27622, 19726, 3453, 18650, 17429, 30575, 30949, 13464, 18979, 19486, 21200, 11625, 21000, 2195, 29303, 21918, 17249, 28639, 18977, 404, 25934, 15592, 31895, 8559, 17115, 9623, 2079, 1336, 9044, 31334, 17763, 23913, 18733, 3571, 19234, 10826, 26978, 5329, 1910, 21606, 11530, 23007, 8679, 19316, 2318, 24655, 14148, 1063, 16706, 451, 21988, 11882, 12471, 17270, 21582, 11331, 11537, 20934, 1453, 4548, 15101, 30404, 20171, 18982, 30613, 14491, 9909, 16187, 19639, 28247, 26447, 15746, 23303, 17356, 15242, 24717, 32310, 9238, 12659, 18622, 31553, 16454, 6953, 16352, 1800, 19085, 11414, 17038, 23995, 9627, 78, 16325, 25445, 2055, 562, 29296, 32465, 978, 713, 13200, 22145, 5079, 19225, 19835, 14027, 11000, 7707, 204, 27181, 28020, 13125, 10281, 1803, 2495, 1182, 22337, 26193, 20715, 28146, 16438, 12858, 1306, 18499, 21036, 26300, 19260, 22814, 8668, 29467, 21349, 30127, 27954, 19087, 14194, 23853, 23752, 32088, 1646, 25835, 7506, 20377, 11403, 1689, 10437, 29035, 5459, 13421, 20486, 19324, 11185, 17928, 2517, 30162, 8865, 17030, 24889, 25854, 15405, 26208, 23229, 26863, 7051, 23896, 687, 21316, 26930, 18270, 14245, 30098, 28775, 21893, 13731, 2457, 27924, 25461, 14682, 10153, 3422, 17921, 24881, 20986, 18257, 14686, 32594, 26032, 21023, 29069, 15432, 22962, 17155, 5642, 29641, 19503, 16531, 23087, 23241, 2408, 765, 16393, 19200, 1116, 24777, 15330, 13249, 11916, 17169, 6887, 17397, 26571, 2888, 16692, 10875, 17289, 4908, 24148, 29977, 20963, 19879, 7102, 17357, 7584, 7377, 8356, 6560, 5577, 2416, 13228, 23973, 16836, 28702, 29661, 10947, 17814, 224, 1658, 22064, 25872, 22543, 1307, 4232, 5664, 3193, 27239, 18714, 4808, 20254, 29615, 6076, 19759, 25131, 2179, 28448, 29574, 14006, 19671, 11645, 9139, 3692, 11203, 6977, 19836, 13391, 27762, 11814, 23089, 18192, 10308, 24329, 19319, 23609, 26564, 8981, 13788, 17897, 1161, 25468, 22555, 22575, 6726, 22267, 1838, 27657, 1288, 9351, 22227, 27739, 32300, 19358, 3979, 32216, 30544, 10551, 17795, 28127, 28827, 9635, 14038, 17197, 27409, 26241, 12480, 11004, 25609, 30825, 11944, 593, 19238, 23150, 20672, 22247, 23084, 27039, 5838, 17351, 24533, 13184, 11161, 7999, 26822, 24814, 28472, 5661, 32006, 17001, 25754, 27973, 8818, 4785, 31692, 24595, 23012, 278, 26774, 3152, 10744, 23881, 10349, 17390, 20764, 11068, 18807, 9518, 10082, 27139, 29659, 12175, 32682, 32712, 17744, 17595, 8749, 19521, 15365, 19597, 29385, 3947, 14461, 22081, 15036, 22206, 15515, 4168, 29348, 24556, 30738, 15234, 25483, 29689, 11658, 10788, 29536, 17143, 11774, 32538, 15771, 10420, 25832, 8312, 1572, 24584, 32183, 12901, 30021, 28975, 26176, 13106, 29764, 18151, 20090, 13466, 14599, 6732, 12209, 17714, 15011, 26319, 15120, 3493, 4934, 29844, 28451, 25410, 3910, 8536, 29185, 2360, 21626, 27160, 26003, 12776, 23359, 10644, 25272, 4529, 21723, 22625, 6075, 6852, 22098, 5891, 3219, 31117, 20932, 20680, 5868, 25535, 4942, 1074, 14173, 28239, 25747, 14570, 1501, 14925, 26044, 26144, 14660, 24566, 29904, 28196, 13108, 29979, 9409, 1959, 22131, 19350, 81, 24922, 15397, 13907, 28752, 10512, 16579, 18411, 16283, 27697, 3733, 1510, 12261, 20245, 14379, 10051, 29767, 3007, 13835, 29741, 28441, 11057, 19987, 30570, 27092, 25546, 30977, 7939, 20717, 2005, 23253, 7304, 18856, 16476, 16995, 25597, 1189, 11667, 32653, 18903, 9064, 18500, 14276, 2805, 9475, 29816, 28066, 19376, 14623, 664, 15588, 794, 13372, 9642, 26427, 814, 9581, 21118, 16722, 10655, 12149, 26163, 11336, 19740, 22775, 534, 30067, 9304, 30681, 32094, 1744, 28033, 20281, 25450, 2274, 4039, 17753, 4220, 30223, 10741, 9219, 12354, 21469, 5185, 12220, 26011, 20767, 9115, 5321, 6084, 18117, 12965, 13215, 19600, 14131, 12466, 15841, 758, 21912, 1519, 29836, 12552, 3199, 3222, 15843, 4394, 29738, 32205, 10061, 30194, 9731, 1279, 15531, 17404, 23313, 6000, 23099, 18179, 29423, 1614, 354, 27036, 7263, 15305, 17183, 1440, 9809, 14864, 15755, 29477, 15647, 29299, 17335, 18617, 29575, 22283, 31716, 26276, 5704, 24154, 6104, 12132, 9962, 1805, 242, 19590, 18191, 28665, 26903, 23653, 26285, 29646, 22203, 14097, 4051, 13961, 29201, 11783, 20163, 13488, 7696, 887, 3496, 5104, 18539, 3316, 8096, 28732, 16258, 7510, 12049, 8110, 29826, 23273, 27243, 11466, 13474, 31508, 23935, 7758, 28497, 29590, 8051, 16679, 649, 5405, 18513, 28221, 24502, 29222, 24371, 25612, 15233, 17637, 11055, 17400, 21467, 22723, 28871, 24707, 7388, 8447, 29349, 19985, 15631, 2620, 27914, 22545, 21807, 7191, 8669, 23096, 26010, 26261, 3655, 15855, 25569, 17558, 11939, 8561, 23237, 16270, 29266, 32040, 30465, 7428, 1114, 6603, 13541, 17298, 17507, 20347, 20850, 23297, 16376, 3002, 1132, 29663, 12810, 23649, 19648, 29147, 25244, 17370, 31308, 29070, 973, 4560, 21668, 24068, 23503, 21268, 23629, 19930, 15361, 3191, 24691, 11364, 18947, 24617, 15241, 19937, 9517, 13901, 23728, 29243, 8801, 9140, 20628, 10641, 2462, 20305, 23086, 14457, 8574, 5098, 2141, 24685, 10960, 30933, 11037, 18746, 31500, 20763, 12685, 24496, 26684, 25907, 17045, 15014, 5429, 19315, 32602, 30526, 28857, 11222, 8345, 7347, 6547, 27367, 13074, 18660, 10917, 14488, 22451, 29394, 2543, 10445, 23574, 29652, 1640, 15134, 22207, 12657, 22319, 16758, 16472, 15425, 26678, 2276, 26558, 25611, 23524, 18486, 6445, 19807, 9795, 6715, 16357, 22103, 21684, 19385, 16936, 7765, 21586, 341, 12116, 11796, 16121, 4453, 10659, 28562, 18349, 20926, 17364, 13221, 4543, 9680, 110, 31065, 11360, 22585, 4563, 17515, 15736, 27750, 16535, 4540, 365, 30065, 931, 31064, 15511, 16658, 23682, 27309, 7154, 22772, 6588, 15672, 18169, 26215, 9967, 15402, 17532, 15734, 6713, 1732, 13636, 10191, 22100, 16581, 16526, 31968, 11413, 24995, 17445, 27097, 25955, 14602, 24938, 16809, 30308, 22919, 16829, 4669, 13512, 15900, 23857, 27893, 20008, 24086, 14051, 1070, 12035, 10983, 23136, 31155, 9619, 30554, 3595, 3463, 4482, 5176, 28669, 6514, 7923, 12828, 30725, 32601, 19279, 14178, 15202, 28676, 9439, 20369, 20687, 21910, 31416, 25139, 16136, 20441, 24313, 17247, 1733, 13884, 24170, 3941, 13151, 18347, 27127, 23597, 1564, 29599, 31250, 1077, 28408, 13665, 11491, 6092, 26310, 9874, 1095, 15673, 12969, 31866, 25307, 8167, 23932, 21405, 2191, 27615, 28983, 27840, 16210, 10307, 9511, 10528, 16725, 4597, 12414, 5960, 32110, 1218, 25775, 26231, 19321, 15434, 23582, 2546, 15951, 18548, 28557, 9109, 493, 11372, 9291, 4081, 15513, 19722, 6131, 17884, 29232, 20195, 13410, 26541, 18288, 14303, 17918, 533, 15697, 2767, 1222, 15633, 17796, 15470, 17886, 20607, 20927, 29554, 3736, 18925, 10952, 3342, 10159, 15097, 14453, 32108, 30772, 23536, 29046, 30037, 28351, 2808, 1940, 28634, 13029, 28248, 31690, 7749, 23420, 21640, 26882, 28877, 4504, 31731, 21861, 26503, 16553, 23033, 19693, 10294, 25571, 28421, 30190, 27318, 117, 5143, 436, 7865, 24235, 5192, 17284, 30263, 27176, 1015, 13059, 9532, 5211, 3095, 25957, 6047, 26655, 8348, 29482, 7794, 29570, 20732, 24719, 22472, 25482, 3117, 24443, 16232, 30754, 22577, 29780, 18149, 6119, 2894, 17029, 13545, 14420, 9089, 2757, 14138, 21995, 8506, 29051, 1542, 15751, 22251, 5401, 20233, 20909, 29656, 32367, 29028, 30530, 19858, 3754, 2983, 30374, 961, 31203, 23077, 23927, 6121, 4871, 26220, 16487, 18586, 17538, 6997, 14791, 11973, 19943, 23405, 25986, 12267, 8726, 7938, 19186, 21509, 17246, 1992, 7080, 13511, 28452, 8270, 23011, 30736, 28244, 22042, 3581, 19632, 31424, 14467, 16775, 2126, 32116, 6465, 7409, 11584, 29431, 9380, 11952, 15960, 8150, 10062, 32109, 32670, 8173, 31409, 29181, 30247, 16067, 5024, 28338, 4615, 22009, 15478, 24103, 15391, 570, 20212, 17719, 11591, 25228, 17325, 15162, 23323, 6079, 22746, 6553, 27653, 19850, 10111, 28946, 14418, 12542, 15360, 23750, 26501, 28566, 22333, 2919, 23676, 164, 19538, 8792, 2759, 10593, 26975, 15028, 13680, 18580, 21449, 3290, 16993, 2473, 18975, 24795, 18431, 3663, 18603, 14886, 23103, 12335, 24653, 31006, 23968, 25028, 23002, 27282, 26797, 31021, 7351, 25891, 23067, 8157, 15594, 13709, 18368, 14814, 27202, 5637, 25856, 15507, 26127, 6384, 30314, 4774, 23221, 16200, 15326, 1517, 9043, 13012, 2084, 19794, 28847, 10259, 27826, 10627, 24436, 4805, 13060, 4492, 3405, 21829, 31696, 25161, 16087, 2378, 26993, 31899, 6443, 29895, 25382, 23732, 17214, 27082, 23406, 12721, 25638, 3810, 15211, 5957, 3715, 18006, 20473, 18642, 10212, 30914, 7, 13984, 2299, 9910, 28779, 12243, 29067, 27895, 32642, 31255, 16978, 21675, 6756, 23114, 24067, 2859, 24731, 1644, 1435, 17413, 17805, 3439, 9212, 16747, 7365, 16617, 22930, 19130, 16852, 14104, 19054, 31965, 29021, 22034, 3538, 11404, 8070, 12758, 9379, 5948, 29220, 30746, 12442, 17638, 17348, 21972, 27995, 9705, 1884, 3953, 5397, 19075, 17277, 12872, 17323, 16788, 31822, 15556, 20777, 3294, 22883, 31235, 25299, 24167, 19956, 2941, 18579, 19256, 756, 23430, 16471, 5076, 28251, 28893, 14498, 21049, 21130, 19033, 24905, 22389, 27639, 28590, 1617, 8224, 31947, 27991, 27516, 14974, 28863, 26976, 13542, 32486, 11637, 25224, 21973, 14175, 11887, 14701, 9543, 5533, 9804, 25950, 11997, 4507, 16610, 26752, 16947, 23823, 22261, 31023, 7654, 14013, 22025, 28190, 19768, 15065, 16032, 7444, 20995, 30885, 22370, 29429, 24055, 11421, 17196, 25941, 1928, 24446, 6381, 18907, 29793, 18879, 22215, 20772, 12532, 4657, 20021, 19076, 4962, 21139, 2468, 2818, 25873, 12641, 1567, 24310, 7097, 25809, 1742, 30052, 20492, 8713, 11768, 848, 17996, 3575, 9736, 3182, 22697, 17722, 6074, 29762, 31013, 28622, 14338, 28397, 30740, 18339, 29333, 31358, 10899, 29305, 4451, 14848, 19228, 12743, 14389, 17057, 8083, 29523, 26887, 24430, 22628, 27051, 17821, 13718, 11042, 8509, 22695, 23959, 23641, 14851, 27968, 11284, 6180, 29117, 13609, 4376, 3082, 15844, 367, 28424, 8813, 15640, 29484, 15894, 16275, 31150, 16390, 20894, 7955, 27797, 13738, 20293, 23911, 109, 12853, 13397, 24577, 5295, 32469, 22135, 24935, 31963, 24598, 25425, 7957, 22362, 23802, 11590, 30615, 4812, 20659, 9366, 21559, 7327, 13368, 11539, 32030, 892, 23756, 3367, 3934, 16763, 3660, 32313, 20582, 15657, 28461, 23547, 30750, 7766, 12478, 8048, 4239, 520, 28945, 25457, 24622, 19173, 29037, 4777, 14356, 23842, 22614, 15978, 12234, 21835, 23456, 28507, 18033, 1179, 31389, 14113, 20524, 16291, 18412, 21701, 17785, 5918, 22125, 23561, 26622, 22445, 31724, 25056, 24185, 22985, 8273, 22664, 5359, 16266, 18858, 15501, 8988, 7238, 16580, 14288, 23854, 30218, 10543, 913, 17660, 15983, 20219, 31016, 8349, 2381, 24797, 620, 5782, 29687, 18609, 15776, 10461, 28404, 27101, 22576, 14412, 10634, 29814, 20797, 15737, 17642, 22402, 12986, 13711, 964, 5307, 12286, 13816, 24721, 22083, 3985, 11931, 22039, 1111, 9307, 5277, 17700, 31297, 18096, 8433, 11868, 15419, 19440, 10326, 12101, 14110, 9200, 7847, 11867, 29281, 26294, 380, 18654, 17160, 6208, 31324, 30702, 9352, 31254, 15663, 13952, 5421, 2496, 11415, 16241, 4553, 11856, 17967, 32752, 28806, 28308, 1870, 15964, 31898, 17927, 14120, 21106, 10029, 22854, 22304, 32260, 9276, 27275, 23424, 6681, 27728, 26167, 5074, 24253, 10990, 18948, 29060, 12658, 1470, 3006, 19896, 438, 31447, 21170, 11919, 22887, 29101, 657, 12260, 22845, 27402, 31986, 11322, 26763, 22380, 30893, 11982, 8772, 24011, 5399, 3389, 29914, 4588, 5267, 13983, 21061, 24468, 27814, 28459, 15514, 10692, 3361, 10766, 13999, 9534, 6699, 2733, 31528, 25749, 3570, 19441, 16810, 21121, 32532, 13160, 21262, 17881, 238, 12885, 25499, 4096, 25388, 26561, 9213, 2806, 22464, 19646, 26565, 7710, 4160, 8316, 10373, 30511, 32325, 2564, 17272, 17317, 20701, 19844, 16655, 2117, 25644, 15488, 28123, 22058, 23110, 8242, 2215, 16012, 12792, 29020, 28271, 27933, 23701, 22846, 1380, 21251, 16219, 25156, 18498, 10957, 21394, 24198, 31803, 8649, 12646, 11325, 24867, 17263, 29539, 21600, 28291, 5168, 12914, 2601, 26174, 3364, 18322, 28329, 27877, 28771, 8897, 5151, 2639, 24753, 4290, 2962, 24505, 18299, 31969, 8263, 17815, 12257, 26445, 18050, 1642, 12070, 10201, 20573, 24613, 14212, 32223, 19719, 19300, 8821, 26420, 29532, 25490, 21169, 24507, 30594, 31919, 24428, 31765, 23818, 8215, 28635, 3300, 10289, 30184, 18308, 32385, 19434, 29153, 258, 12627, 28527, 28663, 9035, 4331, 23095, 7537, 25926, 31301, 5127, 1213, 11899, 25949, 11281, 5802, 15135, 5971, 28735, 3383, 28537, 3759, 16137, 13622, 20516, 26989, 25159, 12976, 12031, 16416, 21209, 5232, 3489, 13708, 15301, 27232, 22712, 12226, 20395, 957, 18453, 417, 31167, 28345, 18215, 13233, 20973, 21488, 16212, 2908, 12649, 20677, 22315, 17531, 28678, 14198, 3535, 31330, 13383, 5153, 19749, 22041, 25437, 9176, 27874, 20167, 4382, 18203, 14374, 27096, 2861, 23244, 9799, 27619, 25541, 19132, 458, 2091, 30315, 19938, 23131, 28269, 25297, 20397, 4849, 8952, 29786, 12165, 32311, 31767, 8927, 2736, 3433, 28098, 20186, 1387, 17529, 8426, 15297, 26217, 16365, 27381, 27810, 16699, 26942, 3627, 19755, 4913, 25829, 1467, 5346, 18441, 11464, 25418, 7280, 19302, 30675, 3325, 11724, 16668, 13340, 3346, 32423, 9829, 6007, 26726, 18180, 31710, 23898, 11298, 25350, 24500, 30732, 32177, 9218, 1480, 31415, 29479, 8972, 5196, 10334, 12742, 30455, 8223, 2494, 9242, 1312, 6802, 20504, 12786, 4731, 17857, 18384, 10578, 21764, 24656, 17930, 21464, 16473, 25275, 1372, 11409, 32347, 12055, 10978, 30039, 30804, 7309, 19416, 29582, 17343, 20484, 3363, 5304, 26888, 4968, 10381, 17257, 13806, 16310, 27917, 32509, 20936, 17152, 8886, 20438, 15491, 27246, 17790, 21572, 26512, 23415, 25681, 949, 8614, 15775, 25517, 21144, 13687, 25464, 7576, 14772, 24150, 12130, 6678, 3923, 32247, 28886, 10173, 1471, 7199, 1398, 13450, 31930, 16932, 31578, 29010, 18306, 25435, 22994, 26594, 6649, 315, 1609, 24462, 16929, 22, 26784, 23864, 23472, 14555, 16171, 31698, 3588, 24523, 5548, 23807, 15076, 18918, 30018, 11941, 8114, 25063, 18231, 2038, 10306, 12064, 15430, 9399, 14841, 25225, 31099, 23812, 9423, 31938, 5077, 13116, 21560, 7690, 13662, 17320, 14247, 23871, 16711, 21102, 21058, 30166, 16180, 9875, 23268, 14525, 2689, 3108, 31000, 5350, 19061, 31725, 30104, 32695, 12821, 32194, 31474, 29968, 5302, 20388, 26997, 10352, 10760, 8620, 3488, 20213, 4259, 32650, 31174, 15043, 4078, 29526, 19746, 14090, 14675, 13369, 7430, 12964, 27904, 23001, 24053, 8225, 27458, 27383, 16319, 14856, 27559, 25194, 12230, 2727, 25081, 32491, 10774, 16496, 5759, 24660, 13676, 29668, 9393, 18986, 23410, 18354, 20907, 14874, 19641, 2648, 28067, 18340, 5508, 987, 23642, 26378, 6801, 3136, 30699, 10621, 16837, 27981, 27425, 30466, 30254, 24733, 15971, 18867, 24662, 10683, 31523, 28972, 5896, 30892, 16685, 2836, 13251, 1438, 24896, 11311, 3870, 20300, 24139, 13358, 21021, 23709, 808, 23868, 9355, 16029, 18458, 26710, 31886, 23429, 28375, 22419, 5583, 6054, 16847, 22310, 25640, 1184, 23274, 14800, 4206, 18644, 15981, 16767, 25088, 22827, 18770, 16997, 28799, 31589, 24062, 4760, 21179, 22763, 19210, 21938, 18808, 31061, 9471, 23892, 5480, 22119, 7500, 13130, 8416, 19025, 32723, 32133, 20824, 19259, 31537, 27994, 25540, 25567, 197, 29913, 28721, 11155, 5219, 26262, 7318, 20428, 2427, 23009, 11811, 32571, 3107, 3583, 16667, 15997, 27094, 13889, 23814, 18993, 2487, 16068, 17883, 20226, 10772, 5526, 27323, 2321, 28063, 3118, 24821, 26280, 31604, 31401, 9258, 11542, 14821, 19237, 29184, 3924, 17043, 18266, 12580, 27763, 15372, 10496, 17606, 8228, 22465, 21282, 27614, 11241, 6360, 15116, 21484, 2441, 12159, 25588, 2361, 31728, 18148, 23028, 23010, 22280, 1156, 14941, 19899, 13346, 18729, 61, 16670, 24282, 12434, 19454, 18818, 11006, 27454, 24274, 18949, 19273, 20817, 17567, 30619, 18537, 13089, 240, 28574, 112, 7536, 23751, 11717, 15010, 19171, 4260, 7998, 10303, 16785, 18653, 24298, 10619, 11174, 27591, 18450, 20427, 18078, 30802, 22941, 3792, 20059, 20049, 17105, 30132, 25048, 29318, 2035, 4455, 4292, 14696, 21274, 6056, 27824, 24509, 19610, 7188, 28867, 15552, 3773, 14444, 14251, 20765, 5318, 21277, 11246, 17024, 12554, 23470, 16605, 23541, 11789, 4759, 22901, 15112, 17307, 17474, 372, 2271, 14476, 8364, 28701, 11583, 15284, 2008, 10260, 14554, 17948, 18492, 20844, 27087, 22915, 5325, 4428, 14350, 24448, 3892, 17955, 29682, 18751, 28187, 7814, 17726, 32082, 15994, 1712, 9459, 9457, 11760, 17266, 21279, 8214, 32464, 14191, 26116, 17770, 28591, 22580, 13366, 23306, 15180, 17007, 20009, 10433, 27250, 21769, 22289, 1600, 15549, 5432, 13800, 2062, 21946, 3324, 18407, 28379, 27789, 16862, 11118, 5612, 14500, 17693, 24022, 21571, 27086, 3633, 32375, 30211, 6771, 307, 11446, 1153, 1315, 15858, 842, 12204, 31668, 13661, 16690, 11085, 2929, 21105, 12217, 21588, 15704, 284, 16346, 17677, 18546, 27788, 8453, 21397, 15985, 31131, 30961, 15714, 9669, 23133, 27767, 12403, 29788, 20906, 20102, 23184, 805, 12476, 878, 31291, 17756, 10895, 15080, 31655, 299, 15888, 10726, 20530, 16799, 7631, 12202, 27073, 27413, 23608, 29375, 3680, 9160, 740, 26661, 24038, 13779, 13535, 20414, 24754, 20248, 730, 25267, 29933, 18394, 13812, 2233, 22504, 25723, 24873, 5986, 16611, 3814, 7869, 14865, 19209, 21299, 21068, 3345, 23343, 27269, 26088, 5574, 30181, 3533, 31125, 17166, 29970, 27822, 19884, 27984, 22036, 20811, 30022, 2384, 15099, 17149, 23507, 22448, 1782, 10884, 13128, 23600, 17018, 9480, 27148, 20905, 3229, 8469, 17544, 3024, 22582, 13323, 27452, 4746, 29551, 25959, 674, 22175, 27129, 5292, 22248, 26156, 15041, 6429, 19588, 14809, 156, 9959, 26519, 17200, 4236, 24497, 9837, 6198, 23722, 15551, 3820, 2537, 7227, 13561, 31954, 32060, 12037, 26951, 4367, 4334, 310, 21916, 19137, 23130, 9913, 19015, 5724, 22204, 14149, 30897, 17617, 9656, 306, 22028, 22452, 22152, 38, 18027, 18689, 28866, 14342, 22181, 7235, 15685, 24678, 27460, 28156, 21583, 17002, 14965, 23019, 14003, 28275, 20948, 23177, 30988, 5861, 26288, 16103, 25922, 2693, 30328, 32485, 10172, 18220, 32046, 31489, 11345, 16563, 5990, 20586, 20273, 3247, 7488, 16072, 4526, 26270, 26019, 20653, 2482, 14029, 32056, 13258, 17582, 29178, 15163, 5739, 27180, 31341, 26736, 26482, 1099, 19546, 18014, 29534, 17463, 18233, 30295, 23803, 483, 19182, 10186, 16382, 25326, 25280, 6406, 169, 16078, 17989, 13679, 17172, 9893, 3060, 14306, 18056, 18029, 1647, 21924, 14273, 28036, 16009, 15396, 20011, 615, 16557, 26411, 32381, 10044, 14432, 18745, 2961, 5411, 25440, 13893, 17768, 30922, 7875, 6892, 28890, 30710, 5833, 25648, 20081, 21643, 29404, 28043, 350, 25074, 23933, 25344, 8420, 30255, 23887, 28199, 19107, 1107, 25601, 23996, 12638, 23708, 31218, 24460, 30559, 25811, 12122, 25339, 9821, 1212, 13856, 4776, 10769, 17402, 8923, 13967, 5048, 1877, 2459, 16167, 26407, 3188, 24734, 24183, 31492, 9368, 14396, 22358, 9723, 19476, 28193, 32179, 4676, 32612, 9295, 25238, 19042, 30435, 17492, 2788, 5977, 3612, 18100, 17052, 7143, 6818, 23272, 23031, 15466, 32207, 13434, 6774, 17358, 15850, 2817, 5836, 21294, 27711, 29655, 8216, 15998, 27873, 17935, 6976, 14783, 27680, 23506, 27307, 23008, 1855, 17936, 2252, 9965, 22572, 266, 2616, 2230, 31371, 11895, 27288, 5273, 8842, 14187, 7383, 13753, 17672, 19870, 1117, 32022, 25583, 17602, 9927, 4771, 22027, 29874, 11936, 27041, 2761, 23782, 2471, 12710, 24017, 23360, 28521, 8450, 30912, 19665, 7025, 1834, 25643, 10387, 21371, 24356, 7442, 5264, 16797, 27326, 9013, 12156, 5799, 21956, 897, 6151, 13562, 111, 27214, 5178, 32018, 18357, 16201, 7094, 3747, 12871, 30273, 30796, 9272, 5911, 14515, 2605, 2779, 8560, 16615, 3009, 24806, 24204, 7236, 24624, 13796, 26195, 6974, 19277, 3374, 30079, 1598, 13278, 17068, 18274, 21075, 13735, 218, 24530, 10020, 6090, 30535, 4350, 1463, 13658, 18401, 10941, 32161, 23685, 15382, 631, 16609, 28226, 24953, 22321, 4007, 11370, 32235, 16305, 28455, 29432, 31490, 25170, 3165, 25701, 17793, 13667, 27265, 31188, 13572, 23094, 3207, 11087, 22136, 28741, 32706, 10857, 4965, 5010, 1128, 32169, 1193, 30536, 18184, 13616, 29084, 8407, 19703, 9586, 16638, 19846, 12265, 28813, 18157, 27718, 30391, 19860, 25666, 26772, 32479, 12562, 14315, 26372, 30464, 16481, 30711, 19151, 4437, 10545, 18143, 20929, 11617, 22907, 16975, 7465, 18839, 15442, 11116, 23098, 7902, 8629, 15553, 511, 31652, 23125, 5349, 17490, 4786, 17046, 26123, 30068, 28716, 18153, 1098, 942, 17962, 12465, 3352, 18567, 22172, 21498, 25431, 22057, 2275, 12538, 27456, 18878, 28542, 28106, 2405, 9268, 21707, 21048, 1574, 29818, 20252, 9504, 26110, 24835, 21681, 13581, 9650, 30203, 31379, 21222, 10118, 26568, 4011, 15770, 22841, 32677, 28311, 29430, 19347, 24482, 31305, 27343, 19180, 31883, 24971, 24780, 29775, 6534, 12856, 21696, 23902, 14795, 32671, 31786, 13305, 18398, 4286, 12802, 10566, 14130, 18688, 32155, 19839, 32472, 22670, 6045, 28976, 30233, 28966, 12446, 19138, 28509, 20554, 30174, 18921, 5029, 3319, 21225, 1960, 16314, 32053, 1090, 2394, 7364, 26796, 24940, 28653, 7870, 8140, 2576, 24181, 588, 32185, 20754, 11926, 20954, 25744, 6620, 20872, 6539, 20436, 3377, 26836, 28061, 20796, 7623, 12201, 17835, 1285, 22616, 21620, 27894, 11675, 20466, 30507, 30397, 31336, 13173, 4979, 8845, 13052, 31289, 18137, 32610, 16412, 5935, 22523, 16826, 24761, 8302, 24615, 12845, 27791, 7292, 16656, 31193, 17788, 6248, 30357, 18236, 24913, 14903, 5212, 18558, 24109, 16511, 27260, 7228, 31397, 31927, 4967, 15975, 11009, 9092, 21927, 5931, 32284, 24843, 23861, 6672, 23832, 16892, 6350, 7157, 14281, 5083, 23982, 23880, 17321, 4702, 30309, 30860, 21146, 13011, 24463, 23829, 13544, 30930, 419, 2375, 29875, 25084, 17102, 21510, 14768, 23686, 28916, 25691, 2490, 13066, 20430, 26760, 18164, 13628, 26985, 21666, 5387, 8427, 23083, 11987, 8289, 12332, 2046, 17081, 11218, 21804, 10244, 30847, 26078, 9888, 31354, 29974, 17037, 10392, 9211, 5442, 4707, 21239, 10577, 3806, 14103, 32266, 21521, 21339, 23312, 17844, 16445, 26026, 13867, 18854, 16818, 14304, 5683, 14563, 13547, 1765, 19571, 24396, 22980, 27730, 32649, 23771, 7060, 31066, 25773, 16166, 24100, 4842, 26247, 15540, 8739, 22877, 18776, 21672, 4710, 14346, 29544, 29027, 28512, 1298, 11596, 16900, 12072, 22146, 11206, 18406, 10572, 24340, 7652, 20563, 18997, 6725, 3190, 23659, 13442, 8025, 21231, 14574, 3369, 2014, 4666, 2325, 16976, 22440, 20958, 14429, 21965, 31226, 16631, 32678, 27900, 4512, 16602, 24388, 17976, 29459, 9207, 17180, 8002, 27928, 8220, 20019, 422, 3785, 21566, 24634, 21014, 22475, 13212, 8058, 11268, 20794, 25677, 27324, 3331, 15299, 16759, 26499, 25641, 17455, 28405, 23385, 16, 26049, 22366, 19540, 25185, 25209, 22751, 13808, 25557, 26008, 6632, 23915, 21755, 16042, 10701, 16149, 18353, 31359, 9580, 9852, 26704, 5810, 12954, 18207, 16665, 26069, 14706, 26145, 12179, 6371, 20564, 26905, 22655, 18155, 20314, 20984, 24666, 25362, 9989, 11354, 19236, 15680, 28046, 15679, 8868, 3937, 28219, 11961, 23720, 30085, 25830, 2555, 21203, 18961, 6148, 4181, 12445, 29240, 31383, 6389, 8137, 8156, 29702, 26380, 6196, 921, 27406, 23969, 13969, 27706, 1246, 5463, 18083, 31809, 22410, 9036, 23889, 25581, 26022, 15853, 18956, 14761, 9794, 3149, 14601, 7873, 4233, 8989, 12905, 9032, 30316, 9177, 22132, 13322, 18748, 31723, 31024, 23450, 21147, 6796, 1196, 11930, 20126, 20636, 31183, 28819, 569, 14872, 12790, 15979, 9820, 14287, 2575, 211, 25890, 6880, 19565, 10162, 24651, 13107, 23820, 5900, 29898, 29438, 14755, 29591, 13840, 24890, 29223, 11306, 7872, 9697, 32746, 31201, 32620, 21095, 685, 22287, 3397, 4354, 29995, 17070, 17846, 15287, 20244, 26531, 7343, 11920, 12374, 6520, 29143, 20058, 24728, 18081, 20771, 20302, 24335, 29104, 5733, 4713, 15705, 13916, 14501, 20881, 13375, 27776, 5282, 32350, 26186, 10521, 18912, 18900, 28691, 16802, 1973, 5846, 23538, 32648, 21523, 32730, 29713, 15943, 13374, 10570, 8353, 14915, 14994, 13222, 12950, 10928, 12479, 13084, 10920, 17382, 20977, 1601, 5839, 19542, 7284, 3654, 8304, 23240, 670, 20256, 27572, 22825, 23846, 26209, 18672, 21625, 20962, 23680, 230, 4177, 5531, 15481, 31660, 16330, 29199, 13583, 22533, 21348, 6333, 28667, 29677, 11883, 16994, 3057, 19263, 30120, 4862, 5498, 10887, 27901, 9878, 26642, 20099, 21899, 30365, 8501, 18933, 11131, 3629, 26415, 27538, 13614, 12440, 18047, 17053, 8000, 28292, 7650, 16485, 22051, 25317, 9052, 4137, 4768, 5324, 13853, 5099, 5166, 13456, 17407, 24690, 16184, 8547, 6137, 16546, 22561, 27363, 16515, 11214, 30043, 6552, 22185, 6103, 3003, 1103, 26994, 32157, 20179, 9367, 21237, 15459, 19004, 16861, 1434, 8435, 22192, 6507, 10830, 5009, 7130, 5590, 22626, 21107, 23053, 18749, 21953, 9709, 21908, 3767, 24984, 2811, 22829, 29912, 25761, 22728, 18573, 13008, 20047, 28700, 7062, 10407, 21324, 2347, 21623, 25839, 10361, 5978, 27929, 5502, 19246, 91, 6687, 25126, 24347, 25265, 5730, 15603, 12604, 29316, 17666, 16363, 8377, 285, 17909, 26227, 21728, 3811, 24804, 32253, 27433, 16688, 24610, 19191, 22961, 2355, 25864, 20930, 20919, 16691, 27464, 29258, 4317, 830, 31745, 6251, 6877, 2399, 29638, 11375, 12762, 7332, 5667, 25485, 22880, 20818, 17379, 14548, 25932, 23063, 6162, 719, 2053, 13473, 13823, 25495, 22505, 31258, 4828, 5139, 18177, 18173, 32589, 9796, 7169, 26352, 28364, 4838, 3727, 22793, 8788, 8959, 25914, 32103, 16126, 25327, 7708, 16301, 16198, 29122, 22573, 24005, 22730, 27114, 13621, 32476, 5248, 23796, 12631, 1690, 22847, 19166, 18214, 3761, 15049, 1456, 25931, 21900, 13526, 802, 32163, 12575, 11663, 23419, 20657, 11405, 24904, 19516, 12900, 2562, 27661, 25505, 27539, 20412, 16848, 8161, 14983, 13617, 21216, 12647, 26259, 22384, 24026, 23157, 20780, 13603, 3598, 16992, 12391, 31663, 3444, 10221, 22610, 5495, 25246, 1109, 12378, 30361, 28655, 1704, 17395, 11703, 27849, 31031, 2092, 25909, 27083, 2266, 23790, 5353, 11111, 14598, 14551, 11443, 17674, 20271, 8837, 12050, 733, 12645, 30996, 16169, 6451, 25509, 12173, 14279, 26788, 31561, 17671, 32158, 8843, 14348, 4192, 8812, 26331, 6317, 28446, 20410, 19676, 27174, 18360, 16786, 26286, 29321, 27610, 6237, 22904, 19829, 18648, 7897, 14729, 1351, 22245, 26119, 9617, 25055, 32729, 7808, 9489, 3481, 23795, 14950, 19007, 26452, 17130, 12441, 12171, 12215, 32232, 31128, 24406, 30706, 20164, 3283, 17972, 11970, 17759, 32137, 31674, 6510, 16734, 1346, 5940, 28744, 29952, 28051, 29923, 19352, 13119, 22347, 11279, 31234, 20138, 7960, 12431, 25053, 19391, 5626, 2905, 13451, 15220, 13217, 31181, 28684, 32142, 30632, 31302, 17075, 15929, 7201, 19462, 20404, 14355, 21205, 13315, 7322, 25129, 9692, 8783, 26455, 6994, 28041, 7003, 27708, 4775, 26902, 22224, 32494, 16885, 13477, 9884, 31210, 30611, 28927, 24762, 25334, 402, 31839, 6132, 32327, 4128, 23544, 9103, 11182, 32696, 5539, 4049, 4262, 26551, 22250, 7822, 15976, 12876, 15122, 27154, 29686, 427, 16143, 984, 2499, 14773, 12904, 15972, 23056, 22274, 16079, 31261, 15379, 14162, 15572, 11468, 10502, 26238, 12058, 27683, 23667, 6041, 4151, 26005, 20052, 15173, 14610, 24162, 11778, 4744, 16850, 16493, 12595, 9235, 31164, 19384, 25146, 12687, 2294, 26625, 4938, 10723, 22406, 5008, 28658, 26060, 12984, 24698, 6674, 17851, 2878, 24667, 14836, 20858, 10336, 9039, 4082, 3541, 290, 2190, 27310, 6779, 2956, 25459, 31408, 32210, 11428, 24632, 30232, 6283, 22007, 24760, 6970, 13264, 10962, 14769, 15276, 22089, 8305, 16819, 25166, 3180, 20922, 3043, 32396, 17293, 11818, 23434, 8665, 19548, 9178, 31141, 20693, 17869, 27676, 21160, 9486, 21677, 20632, 31253, 21855, 19927, 23744, 18830, 16102, 14237, 17417, 21736, 25404, 5670, 14792, 22140, 17872, 19139, 14030, 28438, 13040, 89, 3130, 3090, 32637, 32062, 7203, 6711, 21194, 11497, 701, 18905, 9995, 9751, 30785, 31623, 6142, 17924, 15487, 19758, 22612, 8404, 13018, 20197, 5106, 30898, 10768, 6841, 19962, 25512, 2255, 9149, 31762, 31741, 30054, 12841, 19508, 17580, 23607, 31532, 9208, 28524, 17737, 1284, 4697, 31070, 19394, 29841, 19146, 24625, 22143, 23891, 10088, 1269, 11733, 15903, 9186, 10014, 3833, 5120, 13174, 29286, 24949, 16447, 1514, 32564, 25746, 11272, 21748, 22016, 11949, 16161, 29667, 25956, 21874, 10901, 17613, 12794, 27071, 15764, 20698, 13548, 30805, 16903, 5039, 5484, 6312, 31900, 2166, 7174, 21852, 11193, 32463, 178, 27334, 32566, 28993, 19845, 26073, 7225, 19248, 10667, 18020, 4003, 31540, 28649, 8533, 21850, 1169, 22546, 11512, 22564, 16636, 10859, 17127, 2694, 18297, 25538, 12307, 27403, 3172, 2768, 14335, 7206, 27770, 13246, 2123, 7063, 28754, 27069, 8176, 28089, 16875, 11034, 16919, 8269, 13744, 23553, 23559, 7527, 4939, 8678, 10288, 14546, 4971, 9443, 19243, 24363, 7116, 30418, 14742, 5125, 30209, 20364, 14375, 27204, 18381, 9521, 28040, 22087, 14738, 3630, 23102, 2833, 25071, 49, 3661, 28535, 9895, 20330, 25663, 26366, 17572, 17694, 15172, 26800, 30853, 18150, 26164, 22323, 12506, 13596, 25363, 9163, 18712, 28762, 14891, 18280, 29760, 2965, 4212, 11317, 29176, 20562, 30072, 15331, 24811, 31519, 19795, 20610, 10452, 31573, 14687, 2918, 24018, 29365, 9548, 12545, 21387, 14559, 8251, 31707, 12624, 22928, 28184, 18931, 576, 14351, 22417, 12863, 4600, 7852, 24563, 994, 16954, 30633, 17000, 5479, 9904, 18355, 31979, 10500, 5648, 30053, 3900, 9189, 3534, 7306, 2911, 26471, 22780, 11704, 29531, 19070, 6938, 21066, 24101, 32726, 9866, 26644, 32131, 28695, 30094, 26635, 14088, 26524, 10651, 609, 12803, 17201, 28150, 8515, 23747, 11965, 23247, 13841, 6803, 7390, 19788, 30723, 26274, 16532, 6026, 4401, 22367, 25521, 26348, 9844, 1868, 22797, 29043, 23519, 13892, 3399, 3539, 28638, 13415, 18228, 12183, 19977, 1126, 14280, 9469, 10255, 25026, 20836, 23951, 14869, 21627, 17977, 19704, 22624, 8780, 6606, 9732, 7039, 27404, 29685, 19341, 16379, 817, 19177, 16159, 10841, 9430, 7065, 15792, 87, 14046, 23523, 27410, 16098, 17812, 16318, 2339, 27554, 12655, 16427, 12957, 29678, 30424, 5159, 21825, 21250, 27725, 21064, 10237, 16504, 11884, 3867, 22076, 27500, 7951, 25414, 20571, 26311, 25843, 6832, 8053, 18732, 2602, 22776, 6380, 22415, 24856, 16839, 22791, 11030, 21434, 17026, 13743, 27930, 15102, 13786, 28122, 20208, 23013, 26826, 14070, 28852, 10662, 6095, 29642, 31972, 7543, 11809, 23113, 7106, 9901, 2312, 20140, 22733, 26007, 3667, 12278, 16419, 32551, 13153, 11379, 18130, 18350, 19370, 8637, 13424, 9153, 23570, 18788, 29909, 16271, 24860, 11790, 17471, 24977, 29336, 2982, 8566, 16455, 32694, 5669, 17288, 11273, 32547, 254, 22643, 8004, 8747, 27105, 8950, 5928, 16273, 20283, 14577, 12635, 18668, 7867, 12407, 15210, 19452, 20443, 30552, 4163, 12325, 17658, 25692, 9127, 30815, 14207, 15660, 12150, 4945, 31063, 127, 25901, 7501, 13574, 9801, 15462, 7433, 32428, 19715, 1424, 30631, 9262, 29136, 28991, 24883, 18427, 18773, 6971, 7330, 16930, 9833, 27939, 6909, 19907, 18124, 16901, 16177, 32535, 6592, 8583, 16673, 17326, 32242, 19356, 16622, 97, 27473, 3423, 17426, 15437, 3507, 14922, 7889, 3094, 28778, 28322, 4454, 15412, 25637, 1525, 27290, 31044, 15068, 23687, 5443, 15424, 10010, 723, 29434, 10554, 21889, 14203, 29216, 5901, 6155, 23333, 17360, 15111, 16635, 5382, 6616, 11010, 24875, 26810, 16018, 3835, 32517, 20045, 16700, 11835, 15070, 11968, 6455, 27588, 4499, 21992, 31041, 23366, 5880, 2984, 27186, 16562, 16705, 9454, 26128, 4644, 11184, 27852, 30274, 13881, 12467, 27272, 7913, 13908, 30969, 10152, 17494, 16543, 9231, 29557, 31937, 31073, 16428, 10206, 25477, 11766, 25034, 28018, 7848, 21210, 18872, 5789, 19535, 17278, 25462, 13307, 14065, 23576, 15591, 29626, 5254, 26723, 4308, 20168, 21534, 3326, 25626, 32380, 20354, 9520, 29603, 26656, 10464, 349, 14208, 18253, 9558, 27194, 2475, 6740, 21883, 23602, 31838, 18941, 314, 24492, 18694, 32121, 24217, 29752, 18850, 23301, 9340, 19502, 5145, 651, 21903, 18048, 15114, 5361, 21539, 1680, 5607, 14206, 24432, 6757, 2313, 19052, 11411, 16869, 17716, 32466, 6325, 29787, 25866, 29558, 30696, 16383, 15254, 25117, 9890, 9577, 11386, 4263, 5718, 20203, 8384, 19255, 32085, 753, 20258, 16245, 8953, 28673, 2493, 13913, 26953, 18106, 23045, 28568, 13035, 24783, 4528, 18212, 23140, 30401, 21686, 16623, 29452, 29377, 9949, 7414, 10646, 9986, 14909, 16492, 8578, 16199, 22855, 27408, 12321, 9968, 24570, 30662, 27571, 15493, 17425, 22803, 21557, 3732, 29175, 680, 30277, 7615, 28525, 12115, 31059, 26605, 24764, 7542, 24543, 31597, 14973, 10653, 18483, 15059, 4701, 30453, 124, 30225, 10076, 6160, 14916, 12396, 17823, 26316, 26033, 7805, 19456, 21004, 10547, 13745, 32645, 18758, 22707, 3726, 4395, 510, 18632, 7256, 15774, 11880, 14266, 11498, 20589, 21475, 13286, 10346, 9342, 5357, 21065, 11692, 28812, 20501, 9373, 14299, 27970, 21939, 18243, 18739, 10515, 15875, 16516, 21037, 25799, 23122, 8663, 31834, 16593, 1368, 11509, 32641, 13733, 32275, 6921, 3796, 15192, 11877, 25543, 18794, 21428, 10690, 823, 9595, 20637, 1406, 26617, 27173, 16267, 24712, 5171, 28210, 1582, 5500, 27774, 18528, 6751, 15272, 28473, 3416, 19392, 15783, 13761, 23787, 6643, 10549, 3560, 22878, 22013, 8973, 27211, 2147, 30363, 12346, 8712, 31617, 31554, 22820, 9254, 12824, 18119, 11878, 8355, 30826, 11797, 543, 29845, 6, 3174, 15358, 22491, 21391, 13299, 20915, 2709, 19832, 29289, 6943, 17286, 11439, 11050, 3579, 29861, 325, 14005, 10905, 3783, 22634, 9363, 574, 32166, 22376, 15848, 16304, 30508, 23945, 4103, 20655, 7781, 25882, 20310, 1040, 12660, 14912, 9797, 14314, 23841, 22066, 8640, 17496, 21112, 31833, 21145, 21947, 19658, 16533, 32349, 25353, 26329, 116, 21573, 3268, 30117, 16433, 20035, 23042, 31473, 12761, 8430, 13248, 29828, 16389, 18564, 10864, 16637, 11870, 10185, 3019, 16214, 23979, 251, 19505, 28277, 22569, 25041, 22891, 21192, 12014, 19698, 28079, 9300, 20590, 4241, 10609, 23036, 20357, 1753, 8200, 26075, 8958, 7100, 12842, 14152, 19742, 17229, 29256, 26996, 28808, 11547, 15547, 30481, 17354, 11518, 3368, 17414, 21440, 22292, 19803, 19115, 27563, 11081, 24491, 7695, 1535, 18088, 25861, 22531, 11377, 26676, 2907, 6252, 3099, 20319, 31135, 11436, 7657, 21682, 27528, 28381, 16558, 30136, 15703, 8291, 7612, 27785, 16443, 16957, 10081, 31926, 10246, 9057, 27426, 18402, 3880, 31615, 32298, 6393, 12757, 10182, 13714, 27490, 29291, 25634, 19021, 17078, 19931, 7132, 12766, 28326, 7839, 18071, 29265, 24362, 20933, 5981, 64, 16894, 31643, 14073, 23583, 5437, 13301, 18952, 1980, 30803, 25273, 16287, 31367, 9415, 4491, 18338, 10524, 22842, 17239, 13605, 1155, 16409, 10776, 5222, 2455, 29697, 13355, 22437, 17828, 31539, 19921, 3304, 28554, 18550, 8066, 12830, 17486, 17581, 31777, 12291, 11533, 25792, 14327, 30495, 1119, 32333, 30278, 10354, 8737, 18410, 13932, 17959, 14539, 28285, 22047, 5001, 25163, 23794, 10854, 3842, 22918, 5373, 23515, 8654, 32343, 30107, 28661, 16675, 13033, 27701, 9936, 17058, 2877, 19457, 368, 4243, 17866, 17031, 21961, 16621, 23471, 9281, 295, 30907, 22557, 8043, 24640, 8284, 31837, 7246, 27292, 20068, 22544, 27878, 21357, 31287, 981, 6786, 17101, 19377, 14921, 27517, 6195, 8856, 12533, 10017, 30601, 27891, 12778, 20832, 15227, 4018, 18145, 18391, 1633, 6848, 20346, 12565, 11392, 12449, 17285, 17912, 10693, 450, 22077, 13875, 4656, 30684, 19834, 14226, 31761, 13868, 12585, 5451, 27560, 26172, 27044, 17385, 17512, 9338, 27815, 14513, 30095, 3677, 10110, 19114, 11946, 28777, 16248, 2153, 23432, 1522, 25871, 3206, 721, 1314, 30509, 13781, 27308, 21035, 23270, 9278, 10868, 24964, 17943, 11461, 20669, 9593, 10315, 7378, 5493, 9686, 22679, 3443, 16010, 4110, 1639, 21569, 19823, 26937, 8308, 13433, 28429, 8470, 8588, 17588, 12691, 14866, 26054, 24730, 31019, 23278, 12299, 19500, 15342, 174, 16404, 22589, 4131, 3124, 15261, 12198, 4887, 663, 21944, 11805, 11469, 10775, 5110, 3898, 14240, 15182, 10321, 30332, 11148, 31396, 8123, 8757, 26856, 30477, 12266, 7763, 19222, 13695, 19910, 18301, 21151, 16550, 9191, 10589, 21050, 11795, 19880, 5738, 25787, 7078, 15456, 21491, 11490, 21223, 27346, 18082, 17259, 30158, 31053, 4725, 9706, 12357, 27805, 15586, 19112, 23005, 9128, 5823, 7868, 29670, 15524, 25201, 31312, 20291, 2436, 15637, 22243, 25430, 10448, 16787, 8336, 21609, 26481, 20585, 18310, 29976, 20285, 5497, 933, 14150, 5983, 18206, 4247, 23393, 2977, 15417, 19339, 21747, 9720, 13729, 3041, 24827, 412, 15877, 10128, 1671, 7828, 885, 29940, 15213, 21780, 12838, 11628, 31569, 18408, 24966, 19167, 21431, 22404, 14859, 7447, 18437, 21306, 25405, 19666, 23369, 20861, 21504, 7769, 32175, 4085, 30918, 16548, 24548, 23517, 22993, 11242, 2820, 25300, 22849, 2988, 2051, 6660, 1462, 29120, 28336, 4062, 5394, 10919, 15188, 834, 2802, 2402, 16870, 9264, 25794, 12665, 28416, 1592, 21882, 14963, 5903, 14366, 2596, 24444, 9644, 30241, 32616, 21898, 8791, 3860, 17680, 3556, 10972, 31390, 18139, 32130, 14116, 23294, 16138, 16293, 12988, 25389, 26491, 784, 15599, 15523, 18292, 26338, 2652, 10475, 22824, 32360, 18784, 15576, 11083, 11632, 28166, 29727, 7759, 17528, 9670, 15845, 11475, 17495, 25568, 25278, 12208, 6263, 5658, 13398, 22639, 31823, 22071, 32598, 15708, 19024, 24246, 28960, 738, 32212, 25496, 12591, 21275, 32421, 3471, 22615, 25987, 9209, 10911, 21361, 22859, 14074, 30742, 507, 344, 14390, 28944, 16011, 25274, 9522, 16654, 31801, 1779, 15216, 15550, 11234, 24974, 23334, 13386, 22474, 29249, 27466, 28820, 16456, 25279, 5482, 3211, 19903, 7023, 14509, 10374, 19520, 28561, 18409, 12272, 28567, 8857, 18496, 22201, 1585, 6019, 13019, 21215, 25415, 9675, 28352, 29440, 8622, 15731, 25849, 14128, 19398, 16803, 17748, 17119, 3409, 24360, 15904, 9684, 22006, 5422, 20071, 26957, 18681, 22120, 30713, 9491, 21248, 6466, 21206, 27198, 1656, 26236, 27056, 16178, 5720, 22116, 28605, 15151, 28335, 11426, 806, 23330, 31440, 24151, 22114, 13431, 2272, 28100, 4024, 3911, 18576, 20706, 31879, 5581, 15310, 3901, 7407, 12787, 9215, 12994, 20865, 17848, 16190, 8628, 14774, 12573, 11126, 32002, 30324, 12409, 15054, 9748, 27574, 2753, 13328, 22486, 30311, 17237, 30528, 22920, 32530, 3111, 22379, 1147, 4057, 29770, 13070, 8230, 19371, 16630, 26883, 12016, 9194, 22633, 19360, 21921, 390, 27153, 13127, 4541, 23735, 6967, 18378, 4025, 3958, 23149, 32048, 16980, 14163, 19945, 30260, 29140, 11195, 11967, 8393, 537, 27108, 6569, 11614, 3135, 25305, 14622, 12836, 17627, 21758, 25818, 1976, 8851, 24681, 30170, 22671, 16222, 7050, 2754, 26775, 24673, 26695, 25685, 25384, 32580, 3230, 13797, 7159, 11276, 9553, 30345, 10581, 20935, 3894, 29757, 18196, 20483, 11863, 7052, 7451, 21257, 1194, 10488, 15517, 16326, 14067, 20721, 10311, 14785, 24657, 2996, 15861, 14446, 15239, 10066, 28648, 30987, 31574, 9422, 10975, 23909, 19908, 4386, 32238, 23475, 858, 4525, 32656, 23026, 14885, 15911, 20625, 15823, 17281, 13478, 28656, 17597, 15921, 31646, 6526, 20688, 12540, 11854, 20064, 1619, 19444, 24078, 4695, 28270, 30954, 26138, 22418, 6622, 5208, 16951, 1231, 26307, 17811, 5367, 16855, 5215, 20854, 22230, 18208, 21864, 19000, 31398, 7706, 31713, 16650, 25004, 4152, 7215, 22765, 26219, 20005, 10892, 23300, 3255, 4032, 7282, 26545, 31797, 25938, 30731, 15394, 890, 31132, 31560, 8508, 10870, 7860, 23890, 24030, 12326, 31425, 14263, 11305, 16073, 10113, 12648, 10516, 28279, 16285, 19458, 11104, 28550, 15247, 26213, 10432, 17601, 7502, 1952, 29148, 30884, 11212, 10015, 22981, 21418, 9652, 18186, 31950, 24425, 1165, 24010, 12886, 19675, 15485, 16865, 14934, 20773, 26361, 21393, 19625, 15914, 10359, 16554, 22395, 25536, 14569, 14282, 2840, 22300, 5428, 27512, 23091, 1170, 15803, 29931, 24912, 31888, 19399, 30088, 15119, 17853, 15007, 23257, 6264, 17502, 15381, 17126, 14505, 10586, 11935, 32452, 29017, 16820, 10363, 28319, 9419, 27548, 28739, 19383, 4924, 1871, 16264, 32294, 6290, 15558, 13721, 21744, 11296, 11693, 19875, 2952, 19681, 26064, 23654, 5992, 32408, 7258, 6495, 19119, 11609, 28499, 3120, 18019, 9358, 13349, 3121, 15095, 1402, 5761, 28172, 13308, 16906, 14556, 13701, 26223, 23379, 21127, 6063, 5697, 25113, 7249, 446, 7264, 24813, 21495, 10517, 14745, 32136, 16220, 966, 20372, 1383, 27679, 25064, 10077, 32731, 13859, 13110, 21803, 23228, 31538, 7324, 15996, 11605, 22800, 8311, 15990, 15957, 4936, 26692, 4124, 20689, 3070, 30639, 4035, 18463, 14655, 23907, 4830, 1860, 27238, 23156, 11535, 18695, 16943, 4153, 22744, 18420, 3707, 6579, 15989, 28802, 5811, 19828, 2175, 998, 18478, 6101, 17535, 13637, 30376, 1319, 17106, 27144, 20451, 26741, 12711, 22900, 9713, 19543, 2124, 22594, 7744, 27399, 32402, 4284, 13092, 27608, 28935, 15876, 21636, 381, 12098, 26922, 31924, 20338, 21122, 21722, 11157, 20808, 23052, 16689, 16156, 1389, 13666, 4726, 31911, 10332, 16956, 19125, 27325, 16486, 28600, 14031, 27340, 26505, 31194, 11730, 13909, 8403, 15895, 16124, 21837, 29774, 13021, 6276, 10395, 1079, 30242, 16761, 28616, 10610, 31402, 16987, 12671, 26934, 32398, 17151, 17876, 17238, 20015, 13692, 22481, 2938, 13551, 28881, 11053, 22396, 11190, 23800, 31025, 14308, 10423, 32174, 20447, 20184, 2447, 29870, 7012, 16659, 18426, 10902, 14205, 118, 3614, 14034, 2077, 19174, 22390, 25979, 9771, 15465, 13414, 21269, 14447, 20786, 5082, 9470, 21868, 15235, 21369, 8936, 1362, 30379, 30781, 16970, 31806, 12092, 23928, 26566, 18066, 30810, 10140, 30811, 8195, 26175, 3208, 24210, 27192, 12680, 9120, 28859, 22158, 12934, 16425, 3563, 3757, 19170, 11496, 18533, 21801, 3510, 19299, 13387, 5461, 11492, 30370, 9757, 1174, 29149, 26680, 23827, 15439, 10831, 18278, 15328, 26233, 866, 4063, 13844, 9046, 9864, 31233, 30045, 20262, 32010, 23090, 30656, 14102, 30791, 17640, 25108, 11394, 12866, 14531, 9873, 10386, 31112, 27010, 9880, 30906, 27163, 16239, 19408, 23958, 29512, 12928, 16606, 28958, 27682, 22017, 31631, 466, 20389, 32193, 18518, 22940, 1493, 9755, 19194, 12347, 14201, 2826, 29259, 1837, 8645, 22265, 23433, 19308, 8945, 26573, 28395, 19395, 12086, 16302, 22450, 5807, 2559, 9026, 9596, 5263, 18844, 18510, 24510, 24757, 4461, 13691, 28157, 10465, 16743, 26884, 21247, 14344, 7484, 22726, 26347, 23657, 1530, 16066, 23328, 8840, 27747, 17679, 19626, 16335, 13166, 6931, 275, 8005, 21406, 32348, 25498, 11340, 17352, 6761, 5862, 2876, 22072, 21126, 15839, 30036, 14892, 28480, 11136, 20385, 23706, 29252, 20206, 17440, 28586, 11216, 18698, 29160, 23755, 11513, 20110, 30484, 25288, 14667, 31647, 15611, 1911, 19081, 3800, 31545, 25946, 4356, 30790, 27411, 8748, 5882, 28445, 11767, 3980, 12343, 23516, 26858, 29195, 30198, 4483, 9350, 11248, 66, 29055, 21851, 3938, 28604, 28034, 14307, 2822, 130, 18229, 12264, 7297, 17734, 21466, 10367, 6428, 31527, 23573, 3456, 311, 4848, 19657, 16415, 461, 15150, 10070, 19432, 17607, 10316, 30420, 26254, 18565, 28501, 8059, 21695, 20332, 12168, 23914, 17256, 23465, 28439, 804, 21172, 18305, 17128, 15496, 31770, 19831, 24269, 15449, 6110, 32475, 18647, 2677, 7141, 25510, 14021, 9237, 15131, 17997, 13539, 20299, 15908, 15809, 9753, 5889, 30985, 4294, 8341, 232, 25186, 16374, 13558, 21432, 13571, 32068, 26636, 8429, 27049, 25953, 9598, 29005, 10871, 30567, 20029, 30944, 336, 21482, 8671, 24529, 11516, 18989, 14330, 7350, 15039, 19767, 4079, 32039, 6610, 14674, 29800, 16904, 7832, 27142, 19143, 2928, 28466, 20137, 2860, 24647, 4607, 30425, 27881, 12684, 4147, 10230, 5599, 6138, 7791, 8285, 10190, 22242, 31323, 6027, 28320, 6926, 12666, 4164, 25632, 5481, 14082, 18428, 1136, 18414, 32438, 18181, 3840, 27910, 8417, 26272, 1795, 20309, 7319, 12895, 19584, 22905, 30447, 13147, 17418, 31205, 26207, 27301, 32254, 26492, 24255, 22368, 23337, 17416, 22055, 12827, 31520, 32615, 12236, 23765, 12881, 28817, 24265, 13972, 31878, 28984, 20572, 20074, 16147, 14900, 14220, 28519, 11517, 12767, 30581, 8767, 21597, 5554, 3014, 32470, 7086, 1370, 25263, 25695, 15302, 26306, 25831, 722, 13229, 15309, 3436, 22059, 18560, 31343, 29287, 29625, 14664, 32344, 13869, 23111, 24112, 31552, 1876, 28896, 24621, 3419, 29085, 25177, 4238, 30408, 26210, 8633, 16403, 10033, 10934, 20891, 12880, 4170, 8861, 23730, 26376, 24212, 20185, 30087, 30042, 4341, 7216, 11649, 3720, 21278, 28186, 23939, 18998, 15667, 2162, 15860, 28737, 13126, 3978, 26121, 18870, 6106, 5654, 28092, 21148, 20496, 3739, 12811, 5272, 22528, 28181, 22096, 32113, 25153, 25595, 32577, 9124, 6178, 25670, 9436, 27972, 13994, 14759, 2132, 11720, 20750, 10305, 18832, 13395, 32187, 17012, 17987, 24714, 6073, 3648, 9828, 5995, 11437, 7415, 9756, 11181, 26563, 31654, 12148, 18970, 11607, 4318, 9725, 13122, 29050, 28841, 25684, 4818, 23138, 21553, 24503, 25330, 26441, 8395, 28688, 2240, 6280, 20500, 13492, 10682, 25207, 15212, 19494, 15400, 14823, 17539, 23595, 15421, 13146, 13673, 23780, 17203, 10063, 20408, 8102, 17513, 28337, 23688, 11013, 20867, 26722, 7647, 15181, 29169, 16046, 25157, 14826, 13078, 27827, 4332, 7519, 17647, 6772, 5528, 22781, 11199, 20982, 24453, 30873, 25889, 24519, 30124, 18814, 9622, 25454, 13624, 19745, 7101, 13873, 12237, 9614, 20647, 4994, 21314, 21574, 13751, 13850, 29273, 11229, 15898, 31959, 11826, 26801, 3213, 15081, 17842, 3137, 10217, 17188, 28549, 1076, 2165, 13319, 10028, 26671, 25523, 16023, 25406, 14953, 31374, 19818, 1859, 1548, 9348, 28982, 15669, 20475, 29955, 11076, 28623, 12973, 7024, 16033, 16751, 16483, 17568, 9021, 12017, 15486, 16246, 17179, 23260, 10156, 8323, 5381, 1317, 11112, 9919, 9899, 19294, 30560, 29449, 26332, 15512, 11706, 9445, 17456, 18959, 18950, 17276, 32449, 2949, 2302, 20965, 5774, 13090, 28295, 19270, 6404, 14495, 12411, 17890, 30029, 1796, 10351, 2573, 14949, 8841, 3167, 22787, 27063, 19830, 12453, 27771, 29422, 22067, 7532, 15946, 5913, 16968, 22866, 13379, 13263, 3906, 5070, 19556, 22236, 8997, 21853, 6454, 23691, 8781, 19783, 4729, 21178, 3622, 16846, 76, 9998, 11313, 13486, 17625, 24614, 22690, 5777, 16407, 24349, 12708, 32705, 14842, 19433, 11683, 21385, 16527, 21719, 20728, 15753, 13755, 17219, 15482, 30419, 12063, 1066, 10995, 6222, 31609, 32241, 23640, 2336, 25086, 14159, 1291, 11123, 14289, 3478, 13220, 21395, 14803, 32426, 8585, 12203, 15139, 10472, 18620, 3512, 6661, 31471, 4817, 19754, 13135, 12428, 8369, 32345, 24427, 29886, 30916, 7021, 21161, 23611, 22739, 20296, 28232, 12114, 15873, 19603, 24882, 9991, 1158, 25271, 7842, 21024, 4878, 7252, 128, 20382, 9830, 17055, 7797, 19971, 2100, 16959, 5563, 20716, 5100, 6176, 9474, 2653, 15880, 22393, 29878, 5252, 13689, 29255, 23903, 28835, 4723, 15917, 30177, 31516, 19354, 2870, 12383, 12475, 18582, 16664, 9612, 12192, 1252, 11074, 1081, 5924, 28138, 22521, 10409, 28431, 19604, 9679, 27507, 26694, 7368, 24128, 30102, 21053, 12620, 11801, 8688, 20453, 14849, 31877, 18914, 17121, 13934, 28829, 18324, 20517, 29397, 9063, 21485, 883, 20109, 22352, 7541, 32450, 26791, 14403, 8543, 21333, 21340, 20407, 4614, 29261, 11847, 4645, 7490, 2009, 26480, 28581, 15587, 24079, 10514, 4910, 11843, 29393, 25471, 29672, 13553, 939, 24752, 13167, 22534, 25921, 8862, 11441, 18568, 20924, 22792, 12030, 16854, 11365, 31133, 14239, 26495, 20375, 13646, 19190, 21142, 11309, 13469, 18527, 9540, 20166, 25351, 27072, 15214, 10579, 5265, 30948, 13448, 16457, 9495, 14372, 20952, 27031, 9897, 5968, 22108, 302, 23371, 26242, 19581, 937, 3500, 10213, 21589, 29769, 14129, 15356, 24489, 16107, 12785, 6179, 21790, 27846, 21499, 23647, 26907, 3694, 28482, 31083, 11440, 31503, 18800, 20118, 7607, 24305, 3030, 26097, 29768, 15931, 23904, 725, 22732, 3632, 32382, 31984, 11454, 30991, 28384, 28422, 16402, 11082, 14743, 27185, 12422, 16961, 3119, 25789, 28917, 26662, 32497, 23440, 3258, 4781, 17521, 27524, 5618, 19296, 25933, 29693, 15438, 3045, 1210, 19062, 19446, 14362, 11398, 3437, 16681, 27414, 19723, 3518, 18728, 11546, 12486, 8975, 20827, 1089, 21703, 22991, 29887, 13190, 13320, 17593, 3185, 9296, 9441, 12505, 5597, 23176, 21831, 17300, 28853, 12123, 27237, 13455, 20970, 30071, 12418, 16766, 3853, 16362, 29442, 9542, 15642, 8811, 23358, 16920, 21186, 32764, 2057, 2159, 1931, 19642, 32431, 10329, 29135, 8698, 5179, 17005, 2102, 18671, 3013, 10553, 30780, 16465, 17154, 9685, 20558, 16877, 30135, 17424, 24706, 19465, 12754, 21734, 1045, 4174, 17449, 634, 11138, 5499, 25062, 22327, 12146, 32273, 32411, 10918, 32075, 13275, 16358, 13902, 30809, 18670, 19239, 8128, 14235, 9816, 13724, 22436, 15817, 25036, 15509, 7656, 6419, 14319, 13987, 25441, 24581, 20966, 22360, 15194, 16556, 23852, 18094, 8603, 13199, 26751, 32280, 18737, 18845, 27354, 10858, 566, 24054, 14839, 29524, 23981, 25203, 26401, 27495, 10427, 2781, 5042, 4997, 18484, 20139, 5925, 13314, 17592, 30205, 7598, 24518, 7755, 1484, 28389, 15773, 14939, 27839, 29139, 30024, 22684, 20361, 11892, 23727, 23999, 31521, 21392, 158, 5527, 24871, 25649, 23200, 4384, 31365, 25522, 23563, 10479, 9802, 720, 7183, 3863, 18242, 23030, 3927, 17458, 1773, 25966, 16160, 14410, 20127, 16849, 26956, 12067, 21308, 18659, 13435, 25895, 3994, 9054, 12484, 31921, 10626, 17315, 7835, 22805, 10740, 11613, 11143, 31482, 30339, 8019, 21420, 13244, 18783, 10834, 2419, 17963, 19840, 5795, 17838, 16444, 26672, 12399, 19784, 28203, 9362, 18123, 17979, 1138, 27547, 353, 29649, 20845, 3725, 15230, 25553, 27494, 13905, 32732, 9932, 23801, 14093, 19789, 10965, 5813, 4159, 11731, 28555, 30830, 27444, 15109, 30525, 29409, 31670, 9071, 16359, 12523, 20316, 26001, 23269, 20725, 23040, 24421, 11563, 21377, 20207, 29579, 15312, 21581, 28183, 22487, 31010, 20156, 10522, 11102, 26554, 27208, 18572, 15196, 9841, 21045, 4646, 14744, 5742, 22782, 11259, 15899, 10650, 30352, 4895, 25024, 26732, 3246, 29983, 27581, 7212, 24240, 10194, 17609, 15782, 12061, 11315, 17122, 28380, 10149, 15595, 1764, 28093, 14857, 17994, 7795, 24173, 31568, 14182, 28970, 28242, 2606, 18152, 22903, 1793, 3689, 8401, 24187, 9579, 23427, 31601, 25894, 14493, 19716, 4527, 11510, 20792, 22144, 15280, 18836, 6218, 20494, 5434, 11908, 7664, 13032, 2395, 9080, 31422, 4763, 2365, 4269, 15451, 4420, 15270, 26451, 21212, 14747, 15322, 31863, 27781, 11705, 20177, 5562, 28709, 29382, 4873, 24130, 12603, 7743, 2674, 24422, 30358, 13980, 8567, 24589, 36, 22821, 26028, 28302, 24776, 29547, 24108, 13880, 20188, 29487, 25182, 30983, 15353, 22328, 14252, 1809, 26093, 12640, 6911, 11660, 14678, 3427, 4424, 24561, 32703, 17894, 31449, 29036, 12773, 2947, 26980, 13922, 21579, 30334, 32578, 1188, 29342, 1214, 3407, 17810, 32256, 6707, 11848, 29016, 27392, 13351, 31753, 22722, 29427, 22754, 27388, 29129, 21865, 28825, 1488, 32262, 24410, 10862, 18, 8797, 12501, 10486, 17771, 29443, 17654, 1630, 14884, 12369, 30082, 2373, 342, 18293, 5414, 14846, 30553, 22852, 31621, 19282, 20442, 14575, 29606, 8199, 25183, 6859, 22515, 11277, 21998, 22584, 23522, 20086, 8334, 3549, 32668, 24352, 10235, 9075, 1469, 25558, 17344, 27140, 15766, 9769, 27430, 16236, 17791, 32293, 21915, 23835, 31949, 17885, 31230, 4026, 7109, 17772, 21522, 11208, 20651, 10706, 7885, 8441, 25376, 31585, 12522, 9640, 12511, 32647, 28900, 16243, 29225, 32126, 26514, 9987, 13849, 24732, 447, 29881, 21757, 29718, 10602, 20545, 30883, 29145, 13430, 10949, 6750, 16437, 25993, 18399, 3233, 10890, 13677, 25539, 22894, 1324, 17966, 17634, 9202, 20469, 5476, 9557, 25997, 7069, 2000, 10618, 17577, 27551, 13742, 16831, 30108, 7083, 23182, 19852, 10266, 10170, 24644, 1202, 22653, 9672, 17099, 6245, 184, 10649, 19554, 28924, 32202, 8694, 28159, 19330, 652, 11326, 29942, 10814, 6665, 26762, 23351, 18459, 23310, 31166, 12231, 22535, 717, 26572, 28715, 18994, 27699, 3675, 16295, 7359, 2612, 14671, 27187, 26587, 24552, 24522, 30952, 22513, 8870, 21503, 24317, 26396, 26998, 5068, 14311, 1013, 10003, 29066, 26699, 14416, 25577, 15071, 25857, 16726, 14112, 7454, 20690, 6343, 24337, 1757, 17378, 5816, 19038, 28177, 18628, 15882, 8630, 20295, 16660, 13206, 9739, 6628, 14259, 3949, 32143, 18291, 29559, 13075, 24045, 6304, 825, 22778, 1357, 27768, 4801, 13919, 28789, 20619, 32020, 12985, 1693, 6435, 11270, 10881, 15256, 20602, 15570, 26087, 15146, 46, 26374, 1237, 6754, 16720, 18262, 1197, 11095, 5503, 19643, 25819, 2253, 12450, 29471, 8784, 21843, 882, 29514, 15936, 21492, 23591, 1930, 18707, 4283, 18817, 28385, 9154, 23784, 11003, 30310, 9749, 10183, 12602, 16982, 19017, 14588, 22922, 10575, 16240, 26651, 8122, 7497, 13027, 14170, 14876, 25284, 14277, 21117, 4578, 32044, 30595, 16770, 24628, 2049, 20857, 27944, 11610, 6993, 11776, 5003, 22735, 30854, 25623, 12424, 21390, 15277, 1652, 27958, 18263, 2917, 15455, 10025, 11594, 29714, 3042, 10749, 7437, 28413, 25115, 4338, 13422, 8660, 26567, 19231, 29263, 1901, 12459, 30673, 8718, 432, 25801, 11757, 28355, 6917, 7224, 19731, 17782, 19437, 10598, 15902, 17969, 214, 23606, 22498, 9249, 11129, 4296, 21996, 30412, 17303, 11429, 15922, 16276, 30984, 22924, 28372, 25212, 7445, 10124, 26340, 5586, 11250, 7683, 19102, 24264, 22048, 0, 32306, 13227, 14151, 29965, 17090, 676, 22091, 14184, 15306, 14547, 26780, 28538, 5313, 30692, 24742, 12870, 19655, 14388, 25939, 15973, 16165, 9394, 8068, 20322, 23354, 15373, 16146, 19963, 31798, 29778, 25576, 15464, 12386, 17946, 30539, 9628, 29372, 17489, 32711, 4988, 11994, 1815, 21043, 11894, 1688, 15641, 9925, 12696, 27482, 29425, 22965, 2630, 161, 12795, 25493, 14719, 14732, 18451, 18966, 30935, 10645, 13921, 7928, 10707, 22822, 18570, 19108, 25022, 14879, 24000, 19126, 10997, 28258, 3540, 11572, 13563, 1356, 15193, 2052, 9335, 9390, 19487, 28973, 26767, 17917, 31826, 18581, 10261, 28160, 18980, 6578, 22502, 7806, 22558, 22588, 16908, 13827, 17934, 18508, 10227, 23127, 10358, 15105, 27177, 8755, 12918, 21320, 2893, 22480, 14292, 30070, 32054, 6355, 2409, 29578, 28217, 31941, 10903, 28987, 9148, 10222, 13970, 14822, 14765, 14285, 12962, 23029, 19672, 12309, 22499, 11450, 28214, 11285, 16400, 962, 4637, 31850, 8608, 21204, 5982, 16601, 29227, 30417, 9529, 9279, 11668, 6113, 29308, 11553, 10785, 8612, 6793, 2194, 9851, 19965, 9138, 9031, 18645, 10209, 28197, 20837, 32061, 21844, 11993, 14756, 30103, 26036, 13096, 6001, 7367, 15259, 17899, 25927, 32307, 27836, 6903, 22923, 12284, 14703, 1965, 23938, 31443, 31757, 24259, 32312, 15484, 26591, 16332, 16369, 19574, 28748, 34, 17221, 8320, 12221, 10045, 22611, 27068, 29951, 30584, 28362, 22997, 3634, 14607, 9570, 10413, 27724, 15958, 9330, 9169, 10166, 27710, 16915, 19673, 28456, 31213, 21038, 14445, 6265, 16134, 28339, 13655, 26761, 16204, 27946, 18705, 20098, 21519, 22062, 11756, 22252, 26779, 2110, 10718, 12740, 27020, 3638, 9922, 30622, 24481, 16996, 2251, 10568, 6189, 15575, 11090, 19684, 25291, 455, 30230, 15560, 30096, 30654, 15453, 30406, 31429, 23193, 12015, 32220, 30023, 30652, 24368, 30078, 26645, 31395, 27794, 4197, 17198, 10086, 17711, 14300, 2891, 4222, 20515, 7045, 20697, 22819, 32336, 18942, 17033, 15223, 26337, 23349, 2488, 4108, 32015, 13155, 14180, 10885, 13587, 30664, 14853, 31300, 4787, 13157, 9550, 2229, 18016, 7344, 7686, 29078, 12474, 16629, 646, 18289, 7937, 26813, 15548, 7117, 19939, 807, 11571, 28533, 22621, 21073, 157, 12690, 9872, 22084, 23636, 11019, 11366, 17336, 22147, 26383, 15893, 18009, 25164, 32688, 7980, 23883, 15056, 31048, 22497, 16300, 4572, 86, 22970, 19283, 9996, 7800, 15494, 32676, 17443, 8615, 10873, 20257, 11063, 55, 6397, 27631, 6505, 16008, 32164, 24345, 7472, 19265, 15789, 28528, 18379, 26190, 26968, 25795, 28526, 28508, 4185, 15519, 11168, 3373, 16084, 18859, 27070, 5800, 20260, 851, 26168, 12096, 24608, 30733, 14654, 6330, 10104, 23006, 5700, 17892, 23160, 29224, 22306, 8175, 12119, 2320, 25972, 26914, 18248, 12925, 17765, 7735, 31116, 14918, 4190, 10836, 18626, 13560, 10750, 1428, 13736, 9746, 5326, 4462, 15636, 10685, 18276, 18057, 26875, 13005, 12443, 16175, 30540, 6458, 14117, 11301, 405, 24294, 5011, 31264, 8955, 17910, 12724, 28948, 9116, 453, 14584, 18991, 18592, 14586, 2422, 5132, 5229, 15959, 7676, 30192, 6978, 17990, 8790, 16112, 29420, 31275, 24931, 21408, 8864, 22647, 12820, 14621, 8682, 28349, 7899, 22790, 26637, 16570, 5969, 23872, 3142, 27389, 11725, 31484, 7624, 27147, 32405, 19001, 8003, 27102, 12548, 13068, 14092, 32587, 21336, 23521, 13080, 8241, 7854, 23514, 1719, 22303, 28588, 3391, 6232, 19390, 21830, 30755, 29832, 24046, 12397, 11972, 17715, 30956, 296, 8900, 18911, 18772, 20336, 16559, 10916, 18798, 13928, 12302, 4612, 26426, 11664, 27302, 30287, 10664, 5374, 27380, 2201, 9336, 17145, 13445, 25757, 30666, 27401, 12916, 21235, 14818, 18791, 14641, 24694, 32499, 22914, 20265, 9912, 10958, 6639, 14716, 6469, 4921, 26530, 14254, 8920, 17639, 17645, 15761, 25665, 16328, 23942, 21093, 13271, 23989, 10837, 24785, 16209, 9061, 22441, 24629, 17573, 14572, 31393, 26402, 26697, 11846, 29924, 29589, 27674, 15747, 21007, 10852, 19743, 6042, 10721, 5368, 29666, 8221, 25785, 21529, 16625, 9225, 11912, 29294, 5228, 791, 3048, 5571, 32560, 11914, 16277, 14976, 20650, 15108, 21616, 23578, 1853, 20937, 15735, 6865, 25513, 20241, 14888, 20976, 9027, 9400, 10981, 27030, 16021, 27217, 32740, 1178, 18017, 20535, 13764, 24535, 29315, 20490, 31375, 3768, 29391, 7550, 23759, 26037, 32579, 12400, 2083, 30175, 25845, 14558, 17116, 11661, 7713, 20480, 14380, 22526, 10338, 14441, 10738, 32737, 25769, 22935, 31630, 19122, 13164, 10095, 24976, 21809, 25964, 28950, 18893, 8977, 6891, 20957, 8834, 30823, 24318, 3155, 3491, 757, 30765, 9980, 1253, 19734, 26666, 11353, 10567, 14940, 1645, 31859, 7837, 17361, 18502, 16076, 25515, 24645, 25151, 4242, 7935, 19055, 18723, 8558, 31142, 3308, 32076, 31439, 16306, 21507, 29564, 23972, 26932, 5749, 3883, 32077, 25480, 10355, 1913, 19116, 5690, 1854, 17087, 11130, 11932, 30492, 8279, 9668, 25852, 23575, 25369, 15157, 22799, 22834, 5358, 6866, 881, 31727, 8761, 29124, 5711, 1833, 32058, 14691, 538, 22563, 4926, 22856, 2979, 20702, 13552, 29412, 13295, 11764, 8575, 17522, 16528, 13416, 15730, 6736, 8916, 16484, 27248, 28204, 26802, 27513, 23940, 26328, 3624, 12138, 28394, 7175, 11947, 29707, 27832, 24433, 14105, 13504, 6297, 28968, 16122, 6282, 30650, 29311, 626, 14807, 18480, 28154, 29739, 13625, 7674, 12256, 18045, 28340, 19423, 16092, 19159, 18054, 1505, 8457, 8255, 15691, 12678, 27371, 32433, 4737, 7329, 31304, 4136, 20152, 12569, 15910, 9222, 26698, 30210, 21933, 1983, 26478, 7874, 22681, 2371, 2288, 6573, 17466, 7831, 3836, 24817, 15079, 8647, 2335, 14576, 14002, 29985, 28884, 13408, 9132, 23603, 28998, 1321, 1400, 11616, 25703, 21732, 28577, 11850, 11162, 15293, 19970, 29236, 28495, 30849, 17696, 24475, 4387, 17158, 19594, 31491, 13814, 2760, 23161, 9812, 14339, 18120, 26607, 11135, 16776, 3366, 11603, 7294, 15559, 29892, 5647, 31104, 31118, 2950, 30718, 14650, 6177, 26117, 27099, 20079, 19865, 5141, 19909, 7177, 24049, 8335, 30451, 1526, 17523, 9633, 945, 28889, 12662, 8648, 9750, 3703, 7751, 20815, 19772, 11555, 6207, 13083, 31285, 14685, 9417, 22262, 17333, 31109, 11255, 10249, 13241, 18719, 7222, 17381, 17372, 8489, 10563, 14806, 13072, 4393, 14242, 2618, 11120, 4176, 19608, 19549, 14291, 17265, 30058, 8049, 15337, 18857, 12139, 19730, 20951, 7066, 28858, 10821, 6647, 31613, 22264, 16910, 10739, 19183, 32191, 32204, 17406, 582, 5240, 17412, 13446, 18003, 21099, 9863, 4576, 5294, 3861, 29847, 23900, 14353, 8466, 14236, 7316, 25514, 30874, 26965, 3126, 8163, 6499, 9855, 12768, 25715, 32192, 9744, 11388, 13170, 27040, 31406, 32766, 21162, 24951, 24321, 8618, 27583, 15057, 10571, 15528, 7826, 6765, 31263, 28967, 16781, 16815, 17341, 11702, 2131, 17473, 29269, 21230, 5594, 15106, 23338, 6820, 9584, 12011, 17104, 9603, 28347, 18806, 10671, 6825, 23498, 23080, 24805, 22699, 17636, 25572, 3275, 12345, 27523, 19720, 6488, 20581, 2712, 9882, 19344, 23025, 29279, 7801, 27366, 32393, 31266, 7108, 32290, 23767, 21380, 7850, 22527, 20133, 15428, 5887, 24836, 7988, 26062, 26786, 26577, 17510, 2544, 13630, 18992, 12416, 31357, 15950, 9698, 6745, 31842, 15725, 255, 17294, 27851, 23119, 10784, 27359, 13798, 6788, 19918, 11529, 4115, 10897, 16522, 18702, 13703, 7374, 25887, 4201, 20349, 2981, 5449, 27351, 7649, 25574, 16307, 15740, 32553, 21198, 24568, 28083, 17373, 7816, 9343, 10672, 29150, 5572, 16058, 30722, 29047, 2789, 30152, 12899, 23185, 31189, 11907, 25017, 11338, 28136, 26313, 11356, 11144, 8024, 23652, 25066, 4803, 4846, 26712, 9931, 17467, 27233, 19393, 15942, 5998, 23164, 17971, 20711, 32596, 27316, 20087, 2683, 25862, 17042, 6984, 30323, 23218, 25550, 17704, 21249, 5593, 16195, 7570, 21029, 25985, 24846, 4210, 31619, 23204, 14216, 8617, 8809, 7436, 442, 16185, 5445, 30788, 12815, 13534, 13113, 13268, 29108, 19162, 14475, 14739, 27320, 25704, 23453, 13783, 18171, 11806, 7421, 15327, 18963, 13256, 18505, 21976, 28536, 13133, 17027, 27263, 5902, 13524, 10442, 11308, 13069, 15539, 18491, 14213, 30354, 32698, 21551, 21673, 30020, 18473, 20423, 9022, 26414, 21327, 19053, 15526, 31841, 11016, 25904, 31755, 30186, 9649, 12612, 16547, 32050, 24842, 14595, 13678, 1442, 15283, 24042, 6171, 24426, 16356, 5550, 22469, 28388, 22326, 13306, 27765, 6933, 24458, 3412, 11132, 6024, 2388, 11012, 13343, 26526, 9973, 11302, 28670, 4490, 8816, 19313, 19148, 32672, 29596, 21315, 32667, 25470, 1696, 2308, 23159, 4794, 28202, 14873, 16422, 4829, 12702, 4355, 31058, 4635, 5182, 11996, 23558, 25057, 17383, 16890, 15289, 7149, 8760, 15087, 30576, 30449, 21347, 15668, 20940, 19678, 9630, 23810, 7220, 7754, 18405, 11215, 13304, 10937, 2259, 5620, 19262, 28637, 24262, 15318, 638, 7914, 22253, 10412, 7916, 10229, 16231, 17983, 32330, 16183, 21877, 18256, 15534, 20828, 12966, 2731, 19478, 7984, 29387, 17082, 6528, 8142, 21637, 10793, 4133, 17478, 20119, 8180, 23749, 5043, 24466, 10985, 25762, 19197, 15167, 19471, 15886, 23417, 16127, 18995, 27685, 27515, 13121, 12598, 14028, 29019, 18606, 12207, 4276, 17234, 20431, 15348, 30403, 6641, 7731, 19819, 16385, 21746, 12769, 24947, 10416, 31733, 15607, 32631, 24877, 26506, 30721, 18891, 28071, 6954, 2135, 15295, 10660, 14593, 7888, 5210, 18943, 5328, 13238, 16477, 854, 11039, 10734, 10932, 25528, 18927, 239, 20704, 4992, 12224, 26235, 17729, 14477, 24359, 28005, 14861, 27943, 8777, 23638, 18260, 22124, 12034, 7962, 9887, 1450, 3708, 26627, 24261, 6159, 5400, 15518, 24650, 28003, 16525, 20013, 27953, 31543, 17345, 16988, 2273, 18128, 243, 21187, 14673, 2396, 4899, 27009, 30493, 12498, 19660, 32206, 1313, 399, 16887, 11654, 24711, 29576, 11832, 11853, 22863, 14324, 3051, 29888, 19523, 15001, 8782, 20498, 4911, 14496, 10510, 20445, 10385, 7422, 10262, 13943, 4993, 12917, 3798, 11634, 12141, 25520, 31514, 22756, 6821, 25991, 5133, 11295, 14608, 32437, 22889, 11256, 25733, 22170, 8894, 1521, 22288, 3668, 21141, 7581, 10933, 27226, 16489, 28726, 14796, 26143, 15364, 7476, 6302, 18314, 16817, 18669, 15003, 15274, 13449, 30973, 17279, 31758, 9049, 19739, 31283, 11189, 28907, 29004, 11927, 30822, 12682, 19329, 910, 17175, 8277, 21810, 14192, 19310, 16119, 3685, 17193, 21644, 9506, 28937, 12233, 27545, 17217, 30290, 11307, 22078, 26533, 7286, 17129, 17724, 11, 6609, 6313, 32364, 25918, 20810, 30149, 12491, 17131, 13100, 11851, 17447, 17732, 23763, 23137, 389, 21791, 298, 31962, 4126, 17062, 26582, 20261, 7035, 25906, 11602, 2446, 9607, 17021, 14408, 24325, 26020, 26161, 23648, 6594, 3312, 12512, 13013, 20172, 12111, 23505, 13610, 15934, 24810, 541, 1865, 19003, 8863, 31113, 23874, 30463, 6591, 21782, 4095, 21084, 28785, 9838, 19226, 16075, 26709, 15698, 24465, 20181, 915, 9146, 21638, 2748, 26601, 5239, 25600, 22945, 23957, 29620, 9907, 24437, 19853, 17167, 18369, 9719, 11066, 23819, 12375, 27966, 11713, 14695, 18557, 7037, 17475, 19477, 12185, 12571, 19334, 31920, 19043, 19286, 16998, 11543, 16942, 10280, 24994, 11910, 7400, 3657, 3825, 29262, 31005, 4408, 11163, 198, 2245, 23593, 25112, 29142, 27228, 1996, 28560, 1794, 31656, 14435, 25825, 31903, 16844, 8940, 18793, 23047, 18239, 972, 10825, 25992, 7392, 16912, 6228, 19802, 23766, 10232, 20588, 10402, 15952, 10922, 20413, 24720, 26693, 5569, 11913, 12608, 21849, 26237, 7113, 9902, 19883, 16312, 26287, 14947, 27997, 1139, 20405, 6728, 5005, 7371, 3270, 27890, 18736, 12045, 9333, 11824, 4518, 14472, 6501, 27862, 15696, 18876, 8052, 1945, 12454, 17865, 3966, 28810, 14760, 8721, 17847, 7798, 9391, 13957, 2758, 32028, 32707, 23189, 21922, 1394, 4972, 15047, 20682, 6289, 8193, 23964, 15617, 19382, 20724, 16790, 15854, 3305, 6230, 14167, 28718, 30569, 29454, 27520, 17626, 16746, 21386, 10085, 2550, 16490, 2909, 14645, 5420, 25277, 22063, 16475, 20897, 22702, 8164, 7449, 10702, 2994, 1195, 29688, 17084, 25494, 9403, 15366, 22596, 23356, 487, 10060, 4448, 26992, 19967, 11239, 28137, 10135, 9465, 23148, 18031, 15341, 7331, 9060, 17542, 20519, 15652, 18055, 7279, 16022, 15769, 18523, 12967, 10519, 17074, 10370, 9513, 25003, 20077, 6882, 31594, 1653, 18987, 28608, 23382, 1566, 28573, 16619, 8458, 12923, 18051, 12576, 16627, 16027, 1130, 23518, 26844, 20630, 10762, 30454, 16571, 9943, 19940, 32512, 29414, 6374, 8662, 6819, 26076, 22273, 3125, 21456, 20886, 8338, 22657, 32071, 20755, 13807, 26147, 14692, 7095, 7553, 17536, 18416, 23991, 23259, 26218, 3395, 15661, 1907, 26973, 26842, 21017, 27249, 26278, 12363, 18099, 24969, 16034, 1946, 14603, 2225, 25911, 20425, 13569, 25398, 5723, 25276, 16213, 20417, 13326, 20201, 26832, 14087, 8219, 6835, 18084, 9620, 23402, 22134, 16592, 16172, 14612, 7053, 22520, 25899, 23919, 7945, 7362, 746, 8951, 26359, 13864, 32123, 20859, 4094, 13259, 29495, 14998, 27429, 2892, 22804, 23949, 31137, 13150, 25755, 32342, 16189, 14209, 25266, 13900, 23770, 3837, 2940, 27522, 22911, 29248, 28769, 2886, 24257, 29076, 28977, 15624, 11900, 18708, 10241, 27688, 783, 8530, 31495, 14297, 12176, 27156, 23203, 21399, 21619, 7593, 9105, 23710, 29648, 18466, 31033, 6805, 17620, 5796, 19488, 17947, 16867, 8147, 28833, 19367, 22470, 29807, 14296, 13769, 16752, 30794, 16596, 8701, 18673, 27423, 14982, 22113, 28088, 17951, 3866, 26964, 23929, 28856, 17394, 14704, 13065, 30199, 9805, 9468, 15074, 14564, 14421, 31270, 4086, 3087, 9528, 20095, 20606, 26243, 7405, 12961, 16965, 31145, 10617, 13352, 30712, 20592, 6032, 20508, 19867, 27001, 4469, 21928, 23598, 22680, 6425, 5410, 15757, 22692, 16928, 24467, 10552, 6293, 10179, 27280, 24857, 1554, 1180, 14632, 25948, 26904, 15286, 29754, 12288, 15339, 18472, 26472, 30139, 9870, 15666, 4893, 3722, 8376, 25925, 31622, 18343, 8389, 24839, 6415, 15796, 6250, 5653, 18552, 28491, 17232, 4114, 8904, 32025, 12277, 14573, 27533, 17319, 19887, 26461, 7424, 12652, 3403, 15504, 5949, 8111, 10803, 28840, 25000, 24744, 10283, 29829, 16207, 19229, 4773, 7361, 15938, 21243, 7317, 21097, 6527, 4579, 26373, 12191, 10665, 15363, 5812, 27521, 3353, 29568, 14834, 28910, 3228, 27686, 5464, 718, 12318, 17504, 13402, 24375, 14544, 18095, 8970, 26620, 15602, 31671, 19118, 23786, 29191, 24400, 6829, 20329, 24266, 18831, 17071, 6927, 14514, 5339, 11314, 14238, 30784, 18521, 10024, 25530, 9151, 19365, 22117, 4608, 16645, 7829, 21319, 5652, 6697, 3350, 8796, 14001, 17655, 16969, 17755, 30777, 9613, 8855, 18062, 12697, 8824, 8462, 17280, 2437, 13378, 7429, 29981, 25174, 20941, 22329, 11170, 14043, 22463, 20180, 14937, 30119, 14123, 10877, 10013, 13780, 22258, 5622, 10043, 27420, 8626, 21795, 25781, 11665, 10716, 13055, 31529, 8037, 23736, 17685, 25896, 32248, 27270, 15545, 14566, 28792, 26686, 24539, 11283, 5866, 27673, 14862, 8382, 20350, 6247, 16268, 26369, 3955, 28727, 20742, 23219, 15852, 22623, 12672, 8817, 10026, 29088, 8158, 31195, 9028, 20541, 19591, 3813, 18917, 12922, 9972, 9791, 16698, 2799, 14740, 23107, 6682, 31852, 11697, 25720, 4339, 22817, 24486, 26749, 782, 29507, 20853, 4558, 19933, 16045, 20457, 32540, 25821, 23764, 26180, 19798, 30389, 7515, 25659, 6386, 26675, 18244, 14401, 21342, 32168, 8858, 25765, 31640, 24418, 31608, 23397, 4861, 19545, 14562, 28044, 7990, 6168, 23665, 30320, 24343, 32243, 23316, 7599, 974, 30434, 13302, 9568, 17817, 19687, 16053, 12057, 29401, 14532, 20464, 26921, 9083, 2412, 24066, 18898, 8473, 6055, 14683, 32226, 12661, 28309, 8391, 32629, 2801, 28929, 13765, 22698, 25860, 8715, 3607, 6175, 4377, 31096, 4554, 1073, 15984, 20006, 17643, 4038, 16907, 1673, 30685, 14583, 7700, 16632, 26825, 8556, 13726, 6186, 6949, 4851, 24004, 25433, 9444, 8836, 711, 22895, 28224, 22960, 15015, 14052, 11679, 17690, 20994, 30055, 10931, 18608, 22665, 11581, 18210, 3139, 19573, 14250, 11098, 22727, 17097, 25982, 19136, 17047, 22807, 14098, 17919, 4557, 1507, 12852, 27167, 1393, 12727, 19430, 26706, 3168, 11549, 12338, 19304, 12182, 9763, 22093, 23882, 6870, 15033, 11164, 3027, 18363, 29978, 25570, 15118, 14165, 15912, 27499, 26815, 24511, 8047, 31123, 13898, 11526, 11534, 22149, 27534, 11152, 22150, 32041, 14064, 11391, 6956, 20112, 14741, 27885, 22879, 23361, 14172, 29562, 18761, 1786, 2563, 8820, 30356, 6604, 30716, 5258, 25220, 25486, 25116, 29056, 11666, 20911, 22079, 17527, 29090, 19088, 5298, 11676, 3620, 9201, 29830, 15892, 31603, 19379, 11772, 8268, 15867, 31278, 19241, 21836, 22507, 30850, 32643, 2221, 3357, 5383, 26225, 15489, 13432, 2204, 14407, 12989, 27113, 21181, 9346, 5729, 25340, 25451, 11799, 14483, 454, 26523, 21767, 19113, 17873, 7825, 8570, 14519, 15187, 9008, 19620, 26745, 31400, 13123, 3679, 20804, 15159, 18013, 1075, 4166, 13420, 6939, 25998, 25054, 15324, 1407, 19876, 2477, 103, 28553, 19700, 20819, 4211, 11823, 23128, 16542, 8170, 2951, 9187, 18889, 23174, 14538, 16772, 7398, 32593, 20757, 8138, 30800, 12583, 25150, 18611, 12897, 3026, 24715, 11018, 21952, 13091, 23585, 8577, 18090, 8076, 6012, 2351, 10335, 17761, 11205, 23621, 21190, 8905, 19333, 2579, 32454, 15166, 23447, 16152, 15267, 16007, 32188, 4700, 7111, 23032, 4400, 26490, 20789, 26631, 23726, 7667, 6781, 9955, 17159, 19935, 27698, 29700, 20367, 22074, 9239, 8412, 11067, 27159, 3129, 13728, 27618, 22010, 31120, 9274, 20748, 10979, 19082, 27258, 5615, 21611, 12568, 11334, 24133, 426, 9331, 16973, 30867, 22870, 10451, 31945, 32554, 10483, 6081, 18037, 14917, 4373, 11779, 6857, 26768, 7013, 3170, 27694, 5103, 14503, 16064, 25103, 24993, 4379, 14637, 31232, 11890, 20841, 5684, 11898, 23473, 12688, 29203, 32468, 2547, 1824, 13789, 3776, 12557, 5223, 10323, 21463, 22196, 25100, 31964, 3387, 9490, 12456, 7427, 8802, 26095, 32357, 17800, 27138, 32156, 21087, 9144, 32455, 20082, 13038, 1217, 13941, 30990, 44, 25712, 3113, 10243, 3779, 32279, 28237, 10084, 18053, 17468, 9883, 2265, 29015, 15982, 6837, 7068, 22809, 5121, 31012, 25077, 11249, 10023, 13508, 1846, 8354, 21789, 24739, 31683, 8028, 12340, 14147, 16967, 31455, 24471, 13042, 6839, 16517, 5534, 8271, 7180, 22373, 31524, 5867, 16364, 7959, 11828, 30880, 14967, 2769, 16410, 18930, 14986, 27886, 22439, 10715, 12311, 3064, 28426, 9993, 18358, 24892, 19453, 13650, 20527, 24575, 678, 26397, 11792, 20730, 32057, 1681, 10034, 17324, 21245, 14125, 18869, 2338, 24048, 19144, 22122, 15354, 9536, 3513, 10058, 12421, 12738, 18415, 30026, 16964, 17874, 6836, 2635, 28363, 23444, 30728, 27166, 22401, 30761, 13085, 7000, 1593, 23716, 4446, 13051, 24933, 31704, 16524, 8467, 32047, 2717, 7996, 14057, 11748, 27028, 27780, 25976, 23134, 19218, 29198, 25035, 4935, 17273, 19040, 18984, 5773, 15222, 134, 22344, 5323, 31634, 13193, 22591, 1569, 16771, 26845, 31987, 2170, 10675, 6323, 26619, 18272, 15483, 29699, 28376, 6897, 14914, 27952, 27084, 13681, 2701, 24058, 21664, 3301, 14880, 10676, 31076, 24910, 29133, 27103, 185, 2354, 24812, 11448, 6597, 10457, 13183, 17313, 31875, 22468, 15784, 12028, 10833, 320, 31441, 26029, 31417, 15762, 31925, 24415, 20745, 27742, 28883, 18843, 21034, 12970, 23785, 11606, 24280, 26089, 22263, 29798, 11595, 8131, 5763, 6680, 25216, 2210, 14438, 14402, 21969, 12799, 11621, 22477, 28585, 18146, 11871, 17739, 16417, 16707, 26618, 572, 22987, 7431, 17608, 5170, 12814, 26511, 11460, 30164, 5406, 5276, 13181, 27335, 12304, 7919, 22884, 18376, 24832, 28546, 16507, 3008, 19871, 18327, 7890, 30168, 3226, 7291, 32519, 18068, 20639, 5649, 30640, 26733, 22964, 32605, 29729, 494, 4427, 13641, 29103, 17422, 11992, 28842, 7090, 13443, 135, 12362, 30844, 24862, 31139, 28115, 16162, 25803, 30367, 1829, 24895, 28594, 23452, 28174, 24424, 9221, 16695, 4853, 28708, 24663, 5781, 30228, 20092, 26785, 3460, 16514, 4388, 21525, 6200, 12939, 13447, 17931, 16888, 2697, 7036, 16686, 10284, 26829, 5963, 5380, 29215, 21725, 31288, 3794, 21802, 28333, 15401, 16792, 14805, 28391, 13879, 22600, 9494, 6111, 27502, 6322, 9325, 4396, 1636, 10340, 26346, 6062, 3330, 28879, 16684, 7689, 8699, 23464, 12599, 30292, 25908, 15883, 19527, 9484, 18954, 15801, 4589, 28263, 16176, 23403, 25614, 9402, 9759, 12262, 23626, 5095, 31487, 16013, 28601, 10368, 12873, 10037, 27435, 21120, 16151, 20298, 632, 15671, 7002, 20491, 11691, 19872, 31062, 17822, 5818, 29018, 12194, 18044, 5871, 5671, 19266, 8390, 2356, 24897, 22976, 13453, 8599, 20358, 25104, 8179, 14176, 19378, 21743, 13309, 7325, 12001, 2174, 23493, 24322, 29107, 6663, 14160, 22295, 18264, 19942, 5806, 20746, 7595, 24260, 12963, 27019, 29054, 9413, 11038, 17603, 12756, 27377, 13159, 28399, 30896, 25902, 21512, 17738, 25171, 17992, 5207, 22365, 15604, 5285, 3417, 14323, 19902, 16918, 16640, 18309, 5890, 5453, 19541, 1700, 3752, 15336, 26433, 14995, 9717, 12874, 12024, 19983, 16682, 27286, 1027, 514, 20579, 96, 27079, 31775, 16733, 27833, 22556, 18906, 15040, 31081, 5332, 16506, 10620, 12614, 15113, 20617, 29877, 17459, 2540, 20214, 16762, 16985, 11624, 11327, 19079, 24675, 19562, 25489, 23745, 31094, 27963, 27605, 2020, 30482, 11423, 11745, 13757, 26159, 9917, 24494, 24695, 24203, 29773, 10484, 24983, 26183, 29374, 26536, 16801, 3746, 2121, 32584, 15035, 25125, 7715, 26355, 13949, 30602, 32664, 21524, 4017, 15909, 394, 20348, 9997, 5184, 9886, 25336, 24074, 22683, 23534, 30958, 28097, 1239, 633, 6414, 2297, 7702, 20868, 14490, 4109, 20243, 16377, 662, 11194, 20993, 11988, 29212, 30480, 17665, 10292, 28078, 7843, 30593, 18541, 20327, 20039, 9098, 20263, 2342, 14530, 22519, 26652, 7114, 3072, 29441, 9935, 14049, 12887, 6087, 5112, 19069, 7789, 21687, 24190, 20550, 6577, 9038, 10969, 31821, 11501, 29501, 11540, 13283, 10622, 20062, 16140, 28683, 17150, 7402, 29683, 8366, 1125, 10595, 1034, 16442, 23426, 6078, 17353, 19804, 15956, 28979, 6456, 27609, 9356, 9214, 22068, 20503, 27848, 13986, 29210, 16652, 15566, 17220, 30537, 11651, 17025, 8634, 9952, 26494, 13236, 2227, 19777, 6924, 5255, 1958, 16351, 23372, 3957, 25341, 32590, 32756, 6515, 22485, 30325, 22403, 20131, 3431, 6447, 18315, 18447, 24469, 21042, 26106, 9130, 15527, 9999, 4392, 3421, 1963, 15732, 26690, 29779, 5543, 4273, 28331, 20290, 25016, 6324, 30474, 10880, 5146, 15164, 9589, 29722, 19878, 1092, 24043, 6659, 9432, 29025, 13353, 24156, 13894, 17165, 1033, 6912, 27637, 1975, 30270, 14763, 2856, 13498, 28084, 3250, 30341, 10087, 31515, 22239, 1780, 18449, 18591, 12556, 22026, 4885, 19950, 23772, 12388, 6220, 1726, 14328, 4545, 22788, 14094, 18753, 14157, 18797, 27474, 20758, 18218, 28619, 1204, 19415, 13809, 13915, 14596, 22449, 11419, 1557, 28780, 25137, 7778, 9500, 6356, 31975, 25119, 29706, 31381, 20069, 8482, 18113, 12091, 26657, 25741, 9498, 10310, 6383, 231, 13585, 22770, 23062, 17574, 24327, 18928, 12701, 25118, 30122, 17767, 21760, 32583, 21281, 23895, 16017, 26960, 26216, 4178, 22644, 28848, 23776, 5157, 13436, 16323, 15018, 22107, 31110, 27169, 14808, 2301, 16789, 10151, 20063, 30545, 1487, 13292, 2521, 9636, 15191, 31149, 13254, 28371, 27331, 22324, 24631, 12339, 17389, 17565, 13507, 11873, 11029, 7010, 9045, 4963, 22607, 27471, 21332, 21185, 10523, 12801, 5365, 967, 29437, 4475, 20762, 17309, 28030, 10888, 32679, 18039, 518, 22617, 27018, 871, 13495, 28357, 17721, 2588, 19544, 16298, 15750, 20249, 29362, 20025, 1950, 20831, 30588, 9002, 3381, 2522, 30669, 11167, 28675, 29882, 18187, 17628, 21353, 11655, 6937, 20190, 16738, 23633, 2720, 8006, 9966, 8036, 17875, 18429, 2723, 16132, 6986, 23613, 29338, 21639, 32329, 8675, 28523, 2967, 208, 24091, 16381, 13871, 28191, 559, 20539, 15128, 7760, 5360, 15612, 32624, 8601, 12745, 8370, 24200, 19475, 7133, 22141, 3821, 26600, 7112, 22503, 9621, 27803, 1059, 27093, 23437, 28048, 15077, 4277, 9945, 30113, 10277, 5149, 13086, 9510, 32700, 6532, 18436, 27285, 22299, 17453, 29464, 4419, 22478, 27526, 14971, 27274, 20264, 21472, 13470, 15613, 7818, 14592, 9608, 8948, 5644, 4595, 6242, 3313, 4329, 14494, 28628, 21776, 3823, 19188, 17854, 10054, 23014, 8129, 5573, 22354, 10157, 14710, 16311, 18909, 6842, 17663, 3976, 29777, 30866, 17914, 11097, 14271, 5909, 32627, 18877, 15368, 23658, 9645, 28811, 24587, 20111, 28188, 31360, 15051, 16538, 23214, 5755, 19630, 15906, 20151, 4326, 20988, 21773, 7684, 5787, 14630, 20001, 12732, 8869, 14284, 27455, 15027, 12617, 25915, 24775, 27796, 18607, 8203, 28369, 4216, 8847, 5366, 20147, 18387, 25607, 27149, 29254, 27213, 13958, 32189, 26543, 15125, 6205, 24113, 24283, 29997, 22579, 17439, 30610, 9243, 16899, 16841, 29276, 12458, 7073, 17559, 13937, 24334, 15924, 28645, 12461, 4866, 2341, 7653, 8523, 27468, 13277, 32043, 17725, 30571, 3441, 29784, 953, 4765, 26389, 16309, 4633, 22191, 18052, 30423, 11700, 25544, 19775, 32655, 16249, 15763, 26297, 9778, 11115, 23169, 24289, 11737, 11232, 17778, 9000, 14770, 21360, 9165, 32004, 221, 2254, 28265, 8733, 9831, 28215, 3874, 6071, 10596, 31215, 29944, 10105, 18531, 26713, 20129, 7411, 21772, 30981, 12046, 29480, 5951, 1584, 27879, 382, 21232, 26336, 7486, 17534, 27215, 6869, 32446, 30257, 23858, 18311, 4138, 5961, 2177, 10, 28839, 26293, 28598, 21905, 28011, 5238, 31478, 26818, 15316, 21365, 21259, 11722, 21872, 25696, 20160, 4820, 1433, 23587, 7312, 12982, 15461, 25682, 15142, 30939, 16886, 21207, 23431, 17094, 22123, 18813, 27858, 11070, 16814, 27603, 18530, 23003, 26487, 2804, 15473, 4054, 19163, 8542, 29475, 31018, 15897, 12206, 17514, 8725, 29900, 19607, 3907, 8497, 10894, 17039, 17862, 3628, 31892, 22872, 25093, 31650, 6156, 23925, 5114, 25049, 17819, 18542, 30769, 24887, 19780, 18937, 16889, 6830, 18107, 379, 17384, 18556, 13996, 9992, 10759, 14594, 28961, 11346, 18162, 15308, 557, 2966, 22099, 30964, 31340, 4351, 18945, 9677, 20557, 28783, 31719, 26664, 18873, 22479, 7122, 22394, 28330, 11329, 28578, 4957, 12605, 31459, 1922, 6417, 4917, 27132, 7574, 15198, 24718, 2454, 6677, 20247, 8888, 16737, 192, 5933, 16340, 8432, 32477, 6690, 5057, 14627, 9440, 8109, 29961, 11378, 9442, 14132, 29127, 19791, 9206, 23254, 21444, 26330, 3594, 25810, 14121, 15443, 19111, 13003, 15830, 4258, 4863, 20341, 28541, 27461, 15557, 16372, 11005, 844, 7772, 11352, 21648, 17138, 20143, 10851, 19147, 18400, 29624, 7020, 13045, 20231, 8569, 27382, 8878, 29810, 4366, 16529, 16680, 21217, 31303, 31282, 19165, 21103, 18960, 25242, 14225, 5116, 21610, 1923, 32663, 22226, 6945, 9752, 25377, 5305, 6037, 20613, 20306, 3610, 25390, 10565, 31530, 8921, 31533, 11794, 23389, 8040, 21018, 17978, 20182, 15720, 15029, 699, 27808, 5160, 206, 32066, 16958, 19253, 29998, 30591, 31293, 17386, 22254, 25076, 29925, 11188, 10930, 25309, 32710, 14174, 27121, 3991, 13904, 12639, 10279, 21398, 10669, 8910, 6787, 14431, 668, 32721, 14919, 8015, 16059, 23068, 20094, 17355, 20574, 24852, 19721, 25580, 13380, 3896, 4148, 18070, 11561, 30832, 10225, 15378, 26941, 32504, 25455, 26944, 9896, 13462, 3071, 19345, 5947, 3506, 31848, 13499, 26580, 6545, 26824, 32184, 11841, 20534, 31645, 181, 10592, 1203, 14267, 27284, 21437, 32572, 12329, 5115, 21959, 4631, 20100, 7017, 24236, 3450, 31420, 10196, 28076, 22757, 14215, 31403, 16796, 11933, 19811, 24096, 6658, 16384, 17302, 27201, 25250, 24963, 21997, 5473, 15107, 5552, 14405, 17949, 2207, 2262, 31599, 21470, 28479, 6416, 20712, 2501, 29497, 15409, 24144, 9446, 884, 31976, 16101, 1648, 25439, 919, 27477, 20878, 32467, 7637, 26945, 20030, 15808, 30245, 7269, 28192, 4297, 5504, 23746, 25198, 5060, 16342, 13725, 31973, 29513, 22931, 27790, 23674, 26972, 27379, 26592, 22868, 13105, 4573, 4727, 15728, 23314, 2935, 28800, 7736, 13205, 1318, 2163, 9411, 23645, 13050, 3386, 19134, 23672, 31146, 11433, 18303, 4636, 15064, 14534, 28496, 16989, 6960, 1533, 2866, 8020, 32573, 9881, 8832, 18022, 19063, 32603, 3457, 7642, 10042, 9655, 26703, 17988, 16974, 2672, 15499, 31901, 16421, 9059, 6683, 25869, 9928, 9911, 5582, 20847, 3961, 24558, 16435, 4516, 29469, 9892, 24787, 23135, 13400, 23920, 17920, 21879, 18465, 12913, 4173, 22082, 15251, 6082, 14436, 22325, 11971, 986, 31190, 15694, 3532, 2246, 24024, 21322, 21689, 16230, 18517, 23702, 6742, 10059, 24384, 16863, 11476, 18183, 4087, 14244, 22862, 21427, 7784, 24333, 8259, 26043, 10441, 28104, 25748, 22773, 29550, 30105, 7699, 14367, 11943, 7796, 3337, 15410, 4522, 188, 11646, 24319, 23141, 32724, 30827, 31907, 13968, 30999, 24723, 18637, 11402, 24292, 24076, 17545, 6308, 13768, 10384, 4214, 17516, 22095, 15502, 15086, 7824, 11275, 8531, 26157, 24188, 7610, 5743, 32214, 14135, 29789, 14210, 12062, 7669, 22937, 15083, 9051, 11648, 28851, 26453, 15208, 6022, 4706, 3721, 16375, 11025, 8573, 4040, 24218, 31778, 25371, 12784, 19944, 20510, 9891, 31576, 28824, 2953, 25142, 14045, 27199, 10921, 12544, 32692, 6357, 10376, 17961, 30173, 4325, 28367, 30649, 6587, 9483, 18699, 16044, 1039, 17533, 17297, 7991, 16261, 8413, 10544, 8333, 1938, 30442, 21621, 17240, 8562, 24793, 10643, 19318, 15386, 22060, 15243, 12435, 23813, 16756, 13981, 30586, 17111, 30726, 5524, 4066, 15313, 14933, 358, 30856, 15805, 8794, 24934, 25764, 9657, 4679, 25937, 16197, 26831, 6712, 21603, 11141, 11579, 5460, 1310, 27480, 24894, 8693, 997, 5827, 32152, 3166, 6804, 32520, 26827, 27867, 18946, 51, 14313, 18205, 13231, 29890, 4835, 20916, 16940, 16196, 13656, 30671, 8602, 21167, 13607, 15849, 14133, 13514, 24379, 7293, 3530, 671, 19388, 7119, 7697, 23768, 31525, 29114, 8939, 24457, 6657, 12521, 4070, 25090, 19644, 25694, 23222, 5431, 21273, 19436, 16732, 20782, 13688, 18974, 3711, 16386, 12408, 18919, 28361, 12025, 30751, 8309, 1337, 11357, 20041, 19409, 5260, 15042, 19564, 29799, 12668, 17476, 13134, 22810, 17216, 18365, 6306, 13533, 8521, 6531, 31437, 8849, 13564, 32492, 20174, 6563, 9611, 20956, 4610, 4488, 11065, 20551, 30057, 8893, 20521, 29008, 3827, 26103, 7969, 15699, 8088, 31605, 21863, 12974, 11291, 31464, 6260, 30226, 26933, 26323, 15963, 26335, 27058, 13702, 25994, 8065, 26425, 12968, 30375, 27134, 13393, 10211, 4538, 13839, 21287, 26317, 25010, 11133, 6884, 27795, 21847, 3010, 17091, 15779, 18853, 32733, 19117, 8967, 32173, 17061, 26269, 11729, 7871, 5337, 31236, 24224, 19929, 27655, 30955, 14830, 17697, 26462, 26379, 29862, 16263, 12849, 5609, 17472, 21552, 27598, 6319, 15448, 9659, 19994, 14258, 5063, 23079, 31067, 16253, 13510, 15387, 4368, 13172, 4933, 3797, 26422, 6203, 30630, 14325, 17687, 20996, 8222, 2369, 16269, 21039, 7420, 24036, 24381, 27062, 13390, 30842, 24119, 25260, 9372, 9519, 6596, 32619, 27782, 9437, 14168, 29363, 5155, 17123, 16315, 20616, 8742, 19841, 15046, 11031, 4067, 16728, 10117, 11254, 14056, 1316, 9101, 6437, 17594, 1329, 26257, 14968, 19762, 13951, 4590, 28620, 12992, 4132, 4661, 19633, 21615, 28113, 26102, 28932, 31717, 10123, 24354, 1211, 5954, 18282, 8185, 9068, 30689, 11566, 28831, 24244, 6482, 14100, 11732, 6519, 14459, 10372, 2869, 17618, 7940, 24989, 13771, 21645, 18680, 19972, 17741, 6843, 7006, 9162, 6518, 22934, 28310, 8319, 5605, 20843, 10436, 19996, 13690, 11374, 6432, 13706, 8557, 3504, 28781, 27668, 15228, 9353, 6217, 18167, 30421, 30767, 10421, 14954, 24025, 7425, 25460, 10546, 7370, 11865, 24697, 3562, 2931, 30518, 30172, 16694, 15158, 5828, 849, 13103, 2714, 10582, 929, 2359, 28838, 13782, 23477, 14831, 24431, 9618, 17826, 8607, 17363, 4349, 8873, 10339, 17023, 4328, 6318, 10320, 9664, 18851, 21236, 4135, 24840, 23142, 6670, 13522, 21662, 3404, 27916, 20947, 24562, 19507, 17083, 226, 10661, 3997, 16153, 32377, 28551, 16823, 6590, 15859, 9631, 12689, 3314, 3311, 2937, 19526, 6301, 13412, 13962, 9141, 17678, 19577, 28132, 13838, 6256, 31504, 6959, 11202, 10786, 26843, 29283, 1420, 26923, 9007, 24600, 11139, 21116, 2863, 32458, 27555, 15887, 14326, 10611, 8680, 24206, 8101, 14527, 12507, 4089, 32763, 5415, 26889, 2864, 13842, 26171, 23502, 30529, 30214, 6284, 2640, 25187, 2795, 6749, 31807, 8449, 31819, 29740, 27659, 20352, 31977, 23362, 6930, 14748, 20435, 23677, 15710, 6568, 6044, 3975, 21775, 27469, 23167, 13989, 18059, 3040, 13087, 5023, 23223, 8031, 2060, 19709, 5377, 3803, 12921, 20950, 9041, 15168, 15608, 19998, 32351, 32742, 25738, 14089, 12774, 17965, 18923, 24131, 23551, 29950, 13732, 31865, 13014, 25102, 17787, 14813, 11589, 22721, 2930, 4244, 4303, 16341, 29694, 26766, 24542, 27385, 9433, 14083, 31370, 11782, 21041, 18063, 24571, 13411, 29854, 16648, 29461, 12510, 2854, 22587, 13046, 2106, 22031, 15186, 18329, 14305, 385, 11739, 28434, 6613, 22840, 30720, 17993, 31754, 28264, 18252, 11786, 22212, 4076, 9877, 9082, 11505, 19613, 13956, 5641, 21739, 5576, 14653, 29811, 13982, 27255, 3452, 23286, 11744, 24879, 32575, 22260, 12330, 28462, 17433, 19235, 20890, 21993, 22053, 17080, 5908, 8912, 21657, 1801, 10757, 1058, 4894, 21253, 20075, 32186, 15170, 4598, 16205, 6973, 17676, 27638, 8151, 12124, 3786, 25884, 8711, 13515, 16420, 32482, 25501, 28000, 7098, 19020, 26794, 24454, 20324, 11054, 25068, 17066, 3499, 3645, 27278, 27112, 18615, 26730, 12469, 21022, 7124, 2558, 23069, 2027, 1299, 24490, 25650, 11688, 5878, 30934, 22651, 7812, 12783, 3807, 14580, 11361, 26581, 20434, 30340, 16508, 30580, 956, 30313, 2157, 20529, 32582, 21958, 683, 13995, 8248, 1631, 19920, 11769, 29121, 6420, 32613, 24137, 32390, 1767, 5575, 20331, 5040, 25673, 32614, 9811, 26239, 17048, 8664, 4748, 7461, 12177, 22652, 29850, 30840, 23723, 32098, 1489, 26855, 32410, 4634, 27996, 23462, 32557, 19580, 12145, 21461, 30848, 19041, 14591, 7678, 13778, 16414, 29745, 3931, 10624, 5188, 9544, 32287, 24102, 20533, 8882, 31639, 29613, 29695, 18741, 19653, 4859, 18265, 4875, 2705, 29522, 9857, 16779, 2679, 26837, 16426, 30533, 5747, 28579, 31531, 13974, 14448, 16931, 29853, 26198, 8659, 19766, 31651, 11958, 4481, 30936, 1242, 12607, 13247, 11028, 13734, 27025, 16468, 25606, 2212, 28059, 9100, 9937, 17100, 8829, 7153, 26228, 16130, 1455, 8635, 12770, 28032, 18487, 3358, 9591, 19862, 31054, 8770, 13890, 23884, 18940, 6585, 11213, 31032, 31179, 31535, 18515, 31480, 6656, 18619, 19257, 10743, 2881, 2721, 22953, 9626, 12846, 10455, 21808, 17009, 23217, 16284, 6273, 27835, 2899, 21335, 16780, 30092, 3133, 32217, 9334, 12246, 16905, 6634, 27907, 25534, 13556, 24498, 32394, 21258, 23885, 9287, 4002, 6107, 23866, 18722, 31953, 16578, 428, 27081, 3972, 11819, 12720, 3349, 12781, 6958, 17891, 32150, 8022, 19204, 9681, 21631, 9533, 2921, 4582, 27498, 21227, 31252, 21570, 7920, 16035, 24908, 3086, 12898, 16740, 13815, 18165, 11582, 8350, 21450, 2633, 6309, 20198, 20317, 23476, 13023, 16440, 15926, 16646, 25396, 31046, 13457, 1613, 1994, 24580, 24175, 29031, 29939, 28644, 9112, 12023, 15664, 32127, 13595, 801, 4180, 1256, 28611, 28201, 10257, 11233, 16104, 31022, 6468, 25935, 32032, 17190, 26410, 8461, 324, 25255, 20946, 15265, 14298, 15224, 22179, 29808, 12980, 17176, 29566, 2629, 12125, 15422, 31884, 14134, 11438, 30861, 9788, 30462, 5921, 2743, 18159, 23567, 17069, 30084, 19048, 21297, 5666, 21372, 601, 32288, 5087, 4175, 2498, 32518, 22694, 15359, 22458, 11817, 30224, 26382, 20883, 17579, 21062, 19013, 14200, 29851, 15066, 11390, 29905, 22244, 8299, 16945, 23596, 12047, 7525, 25365, 19578, 5026, 10107, 30915, 5510, 15530, 14877, 18559, 12553, 16254, 32070, 28282, 28860, 22447, 23341, 21422, 25045, 21401, 19706, 17980, 23478, 6806, 6496, 27772, 18612, 2298, 14440, 9674, 27484, 23527, 21114, 9167, 12026, 24649, 1955, 13396, 12216, 9889, 18336, 29971, 16480, 17712, 17681, 16194, 15836, 18553, 15294, 13191, 8126, 7261, 7779, 21460, 29298, 32666, 2127, 8183, 92, 2841, 17808, 31229, 22747, 32027, 13333, 24194, 9015, 22989, 22205, 16077, 18021, 29622, 20276, 11586, 13955, 7353, 3851, 29872, 27347, 11316, 6940, 15542, 8565, 7703, 27445, 11407, 28038, 29653, 17661, 6934, 24960, 18506, 30870, 28025, 24544, 11678, 15176, 24184, 16294, 11297, 28359, 30708, 30919, 18356, 9688, 27578, 26609, 16399, 10656, 10689, 25559, 7921, 22706, 27856, 12117, 6529, 31462, 30269, 21749, 3601, 12667, 15389, 30405, 17268, 27584, 21811, 10424, 14054, 26660, 29381, 29555, 24886, 13846, 7110, 2036, 1236, 17648, 15048, 28898, 7474, 3492, 12162, 6244, 13825, 30943, 4374, 20439, 18202, 8278, 27713, 6069, 23974, 30521, 14724, 30993, 15052, 22275, 22932, 13481, 2584, 16597, 13480, 2903, 15236, 11117, 23252, 16113, 17944, 4459, 12719, 21458, 7565, 9663, 30329, 17941, 20707, 820, 21897, 20000, 7597, 15395, 20694, 9240, 19045, 10019, 18097, 32638, 642, 20627, 27422, 6537, 10694, 16299, 10009, 25332, 29918, 15490, 12642, 28784, 2333, 8145, 21654, 10636, 12816, 6627, 28171, 20505, 13664, 29871, 31788, 5308, 9158, 30541, 29455, 22314, 6235, 10031, 7340, 30388, 8986, 20923, 15686, 13792, 25473, 25124, 31562, 22890, 11690, 7369, 26344, 30818, 816, 5163, 19328, 25394, 22320, 2041, 31256, 23769, 2260, 26416, 10818, 7386, 12107, 18488, 22649, 25114, 31665, 15103, 30620, 20176, 24708, 18599, 13501, 17706, 18734, 6574, 375, 25281, 24957, 21646, 30319, 32419, 10011, 16822, 6023, 25245, 7927, 21195, 20678, 28996, 10121, 26476, 23520, 7608, 13054, 4622, 25556, 30707, 7753, 10629, 18692, 15278, 8619, 1668, 24916, 15320, 32548, 10846, 19504, 18951, 17020, 30202, 8021, 18766, 12323, 5439, 6898, 7552, 20312, 11862, 5632, 19915, 19311, 7827, 17050, 14920, 15643, 30448, 6521, 9615, 14425, 15831, 10754, 10956, 10055, 24306, 18375, 3662, 30090, 22462, 12235, 29128, 8226, 29402, 14669, 32635, 11956, 20400, 28707, 27818, 10065, 21730, 14041, 30006, 31828, 19152, 18693, 29573, 17144, 30350, 15799, 28698, 29325, 10942, 26782, 25952, 11467, 18279, 23074, 25664, 5430, 20301, 28260, 22510, 31595, 3956, 30920, 19100, 6503, 25268, 15658, 26513, 15153, 1972, 2067, 9308, 11751, 13550, 16350, 8261, 14354, 27612, 20898, 13214, 31452, 25256, 2901, 19057, 9654, 14646, 10725, 28931, 2724, 15569, 12944, 20376, 18312, 20553, 32533, 26136, 8118, 18996, 30386, 1625, 4280, 14424, 17248, 8328, 22281, 27932, 9224, 19674, 13523, 21951, 9288, 20708, 31315, 31824, 14264, 7092, 12694, 1144, 25973, 12282, 30938, 8379, 15078, 31933, 7503, 16607, 13747, 4102, 32755, 11642, 1777, 3933, 32418, 24132, 29138, 11269, 14910, 15795, 20885, 6268, 13483, 8604, 2523, 9879, 9767, 24157, 17434, 28513, 8710, 11615, 10822, 6105, 21079, 29605, 11556, 26229, 12007, 8133, 31091, 26812, 8071, 32487, 27869, 10267, 32104, 19106, 20396, 15240, 23187, 9418, 24725, 4797, 1365, 4837, 26689, 24034, 22759, 761, 19123, 25604, 28021, 16879, 13195, 17092, 19192, 17306, 26252, 28965, 18757, 11963, 18195, 22552, 17816, 13376, 6229, 23666, 6481, 1682, 16949, 5012, 7323, 13010, 15367, 8504, 18679, 24165, 12977, 5579, 22121, 17798, 21502, 1579, 18820, 30369, 9326, 29111, 22493, 25294, 11480, 22008, 10175, 19051, 24830, 30614, 19515, 24153, 27709, 7520, 24945, 32542, 3604, 21412, 23555, 13598, 32510, 3809, 4156, 4551, 12313, 25447, 26384, 20928, 2256, 16494, 14782, 18778, 17932, 25097, 27193, 25373, 26719, 7259, 17824, 32636, 18476, 2184, 29927, 25237, 17797, 31939, 21088, 23975, 31686, 29230, 22438, 2450, 9377, 4889, 16749, 30863, 19325, 11710, 19091, 27223, 1375, 4093, 27387, 16663, 31222, 30483, 25401, 8995, 29204, 21777, 28902, 15231, 31864, 18532, 20512, 28366, 2202, 28760, 23830, 10735, 27048, 29822, 9950, 17224, 2531, 15630, 32301, 1934, 31813, 30196, 32107, 24803, 5689, 27054, 20455, 22357, 12090, 12127, 16418, 21307, 1007, 19656, 1177, 25654, 24029, 23374, 27985, 131, 14711, 22654, 30118, 14393, 1251, 21537, 14649, 6320, 17359, 23976, 15695, 4196, 6874, 860, 11765, 11775, 17833, 10686, 17702, 18676, 14946, 780, 5962, 352, 316, 4016, 16164, 29040, 25847, 19651, 23060, 32722, 24829, 20144, 15988, 23562, 17789, 15317, 2592, 9309, 2349, 15716, 20713, 23261, 10318, 11699, 31084, 5976, 15819, 15832, 13821, 21481, 24094, 18824, 2740, 29470, 22210, 15340, 14929, 3891, 20876, 29039, 13644, 5768, 8744, 7505, 4995, 29897, 30187, 12555, 27133, 26854, 18855, 15362, 16005, 22285, 16297, 2241, 11147, 12462, 30330, 19208, 4021, 14340, 10988, 2655, 20863, 13274, 23198, 870, 4722, 6351, 19509, 21304, 3286, 14689, 18131, 3224, 10247, 6593, 15045, 13097, 17703, 20692, 22164, 18691, 11803, 82, 10487, 21176, 19083, 14529, 30626, 5378, 728, 1561, 16054, 28090, 20776, 22001, 24116, 32073, 5855, 5297, 26598, 19482, 19366, 3926, 26604, 30438, 27784, 3844, 24239, 14010, 16551, 23466, 25728, 10562, 31855, 18780, 14850, 18501, 25492, 31629, 20605, 12013, 18547, 1044, 14480, 22388, 31412, 10616, 13423, 10501, 28024, 22180, 17750, 32738, 30001, 4161, 30409, 19799, 26187, 25337, 17079, 6948, 16568, 3262, 5322, 24456, 19627, 3261, 21240, 24279, 11264, 31549, 27501, 23550, 27889, 15019, 30229, 20679, 27978, 30347, 15345, 30059, 10927, 12854, 19028, 27061, 8926, 877, 13111, 10375, 25002, 14084, 10491, 4844, 10561, 27417, 26659, 6581, 16950, 19213, 18910, 25466, 5922, 23788, 22011, 5280, 15435, 6144, 965, 29499, 29858, 21506, 16784, 32514, 14553, 32528, 32069, 26550, 18287, 16813, 28370, 14977, 27190, 6401, 8814, 28677, 17985, 10936, 15136, 6031, 31831, 24440, 20821, 4495, 20670, 17142, 24095, 21542, 30668, 29219, 22308, 14827, 29719, 24997, 8750, 12632, 30753, 31319, 22909, 29208, 6233, 5514, 23715, 26438, 4097, 20525, 11820, 22461, 31502, 29711, 27989, 22864, 13756, 12942, 8697, 15058, 31735, 4203, 27541, 23191, 23741, 21214, 9915, 28911, 27876, 17292, 32322, 24414, 27735, 24605, 23923, 20744, 3646, 11253, 918, 27847, 21925, 9173, 19638, 17878, 14415, 28257, 26305, 29749, 22524, 14735, 31431, 10473, 5486, 29842, 22554, 7911, 28679, 32521, 15649, 29680, 8676, 20683, 24639, 21233, 30734, 26009, 31075, 15178, 7941, 3683, 10499, 25109, 4778, 17171, 18211, 10865, 26871, 18861, 10590, 1995, 19868, 32640, 4357, 23937, 3442, 27241, 27336, 27360, 9369, 13427, 18269, 29058, 14, 4684, 25975, 32112, 31051, 28293, 8642, 9825, 25381, 5496, 31501, 24702, 5873, 15067, 16946, 1446, 2283, 28939, 14847, 24377, 23508, 13494, 30801, 23912, 20961, 4890, 16014, 30267, 17733, 7579, 16347, 7569, 6706, 12163, 24749, 122, 32051, 13015, 13267, 28212, 13325, 12142, 7134, 16521, 12401, 11623, 28662, 7173, 14810, 12427, 18731, 31726, 22097, 17332, 16173, 4891, 13651, 8555, 8980, 7144, 30075, 3672, 1545, 5691, 17991, 6124, 17503, 5849, 25037, 18957, 29728, 9559, 20051, 1247, 11727, 23905, 5301, 15810, 13594, 19348, 7617, 23628, 7819, 32132, 32358, 8830, 13878, 11671, 13832, 31136, 21765, 18765, 29730, 10091, 25009, 13242, 25029, 15538, 12730, 25868, 25968, 32524, 13750, 21071, 18999, 2315, 15335, 27843, 21514, 13276, 5183, 15096, 20205, 25467, 11545, 12563, 18834, 8014, 28303, 5013, 17275, 17305, 8639, 18290, 18348, 27945, 18881, 16228, 21443, 3945, 15288, 31769, 12457, 20866, 25877, 31577, 27419, 13459, 13136, 25038, 22789, 9776, 13298, 20477, 26522, 21193, 5379, 11113, 17231, 2913, 24903, 6783, 29032, 15742, 17630, 22982, 23463, 14549, 31410, 9939, 5483, 30992, 1873, 18122, 25657, 29922, 14154, 4369, 69, 29014, 18474, 28711, 6035, 28994, 12094, 27344, 3177, 1395, 31870, 32286, 16871, 9906, 25270, 13615, 14392, 19710, 19617, 21550, 17667, 30953, 17566, 16599, 11226, 9715, 13593, 13468, 30154, 9689, 31072, 8915, 12381, 11287, 7170, 9256, 19301, 9117, 16086, 9171, 27047, 29116, 22548, 13634, 24527, 8306, 15115, 23265, 163, 1691, 17675, 16047, 29002, 8282, 25229, 23467, 10231, 21355, 11669, 6295, 16037, 11312, 31507, 10898, 15025, 30364, 31165, 14079, 5892, 8127, 14540, 15655, 21264, 7557, 28435, 11459, 21711, 22221, 32267, 3522, 18435, 32651, 25646, 19928, 25564, 6542, 16168, 19401, 11151, 26390, 21799, 17956, 15403, 21527, 6668, 24929, 3424, 28189, 27872, 322, 11078, 10403, 9516, 18246, 11481, 15237, 32622, 17635, 6185, 31794, 6992, 14516, 16768, 17134, 15650, 32295, 21964, 11341, 1292, 14725, 20668, 32480, 13668, 27240, 23394, 16572, 15584, 20507, 14778, 12875, 4300, 4225, 11519, 21325, 15878, 29073, 30908, 27532, 8238, 914, 8169, 22603, 5407, 29594, 14217, 8085, 22020, 14928, 31922, 25886, 8455, 27817, 23712, 21698, 24031, 3105, 16530, 13329, 19450, 29884, 8600, 31056, 22506, 30146, 11966, 5674, 22128, 25719, 11166, 5290, 29771, 28378, 6448, 29520, 9305, 3498, 14930, 31534, 11260, 28894, 16155, 25578, 7423, 20902, 7539, 5244, 12867, 22565, 4456, 25635, 18041, 13025, 16560, 9317, 3769, 9571, 11869, 346, 8587, 4036, 11726, 26475, 23395, 16644, 27313, 8866, 614, 20286, 21208, 25217, 28682, 17393, 7289, 20918, 15245, 16394, 22112, 21199, 12327, 8132, 6123, 27476, 31751, 19185, 9946, 28529, 10771, 13831, 9111, 15525, 20699, 7230, 31624, 26470, 10839, 27801, 4129, 6353, 10137, 31780, 21328, 27235, 16247, 25753, 30429, 8735, 5886, 10362, 17701, 8408, 11180, 27184, 21489, 3189, 19474, 21886, 29009, 29848, 2713, 2632, 12210, 27450, 14142, 25239, 23132, 27586, 10239, 6004, 5696, 17849, 22343, 17409, 15215, 8527, 28530, 5919, 13822, 21128, 17163, 3882, 26808, 15582, 15562, 18897, 24823, 22126, 2925, 16322, 7648, 11332, 17550, 19034, 15148, 24378, 8495, 20603, 19036, 23232, 27374, 2070, 16355, 9683, 27315, 26557, 13848, 13944, 10327, 30879, 2203, 18161, 26258, 843, 16223, 2853, 22761, 5634, 5989, 24849, 28909, 16495, 16252, 25916, 3688, 13129, 28014, 12131, 24967, 7181, 30520, 836, 30281, 6671, 22627, 923, 8646, 20743, 8623, 14975, 21966, 21990, 10729, 20235, 25235, 14952, 14913, 20691, 26458, 13654, 12160, 27575, 7412, 3474, 25105, 15571, 25319, 24784, 20096, 7379, 13405, 29257, 23124, 7027, 27695, 4877, 25221, 12932, 16408, 8264, 10012, 11235, 12894, 16501, 29945, 30550, 15269, 13601, 10541, 16181, 8674, 8406, 12597, 31154, 8992, 23546, 32685, 9786, 12530, 14707, 714, 11024, 27597, 6913, 14202, 30757, 22836, 26399, 2442, 18969, 6807, 6373, 23944, 9908, 21902, 7466, 8460, 5167, 23497, 15840, 32397, 4571, 1812, 19833, 10526, 9572, 20028, 26004, 31216, 6009, 31089, 16108, 12996, 10914, 7931, 19101, 12379, 13058, 20814, 1262, 19995, 16753, 19702, 26756, 6174, 14715, 16639, 4264, 24193, 10767, 31550, 19429, 31657, 18092, 10705, 5657, 2108, 26626, 28116, 25145, 5390, 19953, 9455, 3473, 16026, 24759, 10497, 21842, 24755, 13897, 4274, 18235, 9323, 4323, 21423, 22811, 4187, 21712, 22995, 32444, 27365, 25247, 6584, 31946, 3448, 1686, 21608, 1948, 30947, 13439, 13317, 2644, 6310, 9327, 29355, 25101, 22767, 27332, 25788, 5856, 7982, 28651, 6399, 6457, 8063, 9096, 16488, 2879, 7127, 15614, 21272, 11416, 22459, 526, 13204, 24918, 23886, 20004, 9978, 24374, 28714, 20882, 14665, 19847, 27960, 11263, 23489, 29044, 14896, 21975, 3158, 2587, 293, 15889, 21904, 14542, 32432, 18219, 14647, 27304, 12524, 16392, 10345, 14618, 24164, 2103, 27052, 31349, 15619, 449, 28964, 17113, 21983, 22529, 32626, 12884, 30436, 18932, 8165, 9406, 8598, 17340, 18247, 24866, 24304, 4068, 21473, 13791, 11349, 31558, 7123, 16510, 28901, 22374, 11321, 19417, 31238, 4411, 10254, 20793, 17986, 18743, 24851, 30504, 26277, 12463, 4506, 10132, 11225, 18522, 376, 25859, 1050, 24532, 21740, 5197, 247, 14382, 3877, 15541, 29827, 10964, 12633, 4398, 23227, 22674, 28514, 22500, 14652, 28971, 18438, 30296, 23055, 10658, 5953, 12213, 5843, 16063, 18926, 21854, 7786, 18690, 6810, 21731, 12214, 32701, 11294, 25469, 31792, 14085, 26535, 6980, 4647, 22950, 22672, 16643, 18134, 22873, 20419, 3025, 25777, 18549, 5650, 143, 14908, 19060, 31182, 26324, 777, 7621, 26629, 17017, 20374, 10218, 9179, 7944, 5148, 10101, 14037, 11474, 12502, 26510, 29635, 12577, 5391, 3016, 19276, 27720, 9397, 26668, 29351, 30621, 1426, 932, 10924, 9137, 12520, 5435, 6315, 15785, 19824, 9093, 10444, 7265, 6169, 3531, 21494, 13537, 13942, 15646, 23208, 21056, 22453, 31736, 6412, 31472, 14757, 5250, 7583, 14679, 24284, 16718, 18635, 27125, 3438, 23897, 17562, 23446, 29885, 24152, 12301, 8704, 5283, 12305, 17543, 15244, 7729, 5515, 5804, 30647, 16401, 30986, 7851, 17211, 11318, 16618, 24550, 5468, 29000, 19179, 1822, 26388, 15388, 19598, 23129, 3115, 9761, 6758, 17483, 13354, 29948, 7792, 13088, 19680, 31251, 10027, 9926, 9842, 10169, 30635, 971, 22517, 12371, 21716, 31186, 24807, 8554, 1275, 13582, 3605, 10007, 14075, 18005, 23413, 27546, 30574, 11335, 16144, 4535, 29679, 25826, 5916, 26860, 10200, 18160, 13813, 21110, 18176, 17850, 23126, 11172, 4034, 31372, 25080, 18489, 29785, 31680, 17308, 19250, 32289, 26952, 10811, 30683, 15700, 2939, 21417, 11791, 7082, 1198, 10533, 20942, 8227, 22169, 7841, 21833, 3858, 25368, 31808, 13606, 30393, 8229, 26002, 8136, 31771, 10429, 14204, 28697, 24693, 16774, 22874, 21152, 1060, 3758, 27236, 15260, 19272, 30737, 30312, 19342, 7126, 7337, 26688, 18685, 16324, 28874, 19332, 26744, 7217, 6544, 30056, 155, 15554, 7517, 28756, 11330, 9205, 21359, 15476, 10535, 12417, 20756, 9182, 5455, 18116, 5369, 21870, 19201, 7084, 26628, 10954, 3774, 20292, 9055, 3245, 23469, 4256, 1309, 23220, 30346, 24659, 13413, 15977, 708, 25154, 24476, 430, 2525, 26894, 20115, 1906, 24737, 7732, 896, 21669, 5492, 27908, 19506, 20351, 30272, 12676, 9066, 4947, 16388, 14071, 24111, 24930, 21679, 14837, 30532, 13684, 11359, 20370, 27463, 31961, 18768, 13118, 26205, 32567, 15715, 2367, 12650, 26077, 7305, 4320, 32096, 26142, 12987, 16043, 6548, 31069, 9302, 585, 30598, 27589, 26963, 7031, 3649, 22111, 22881, 10414, 13363, 19419, 27120, 14253, 16880, 27899, 29405, 2500, 26339, 28353, 19006, 10147, 17717, 23548, 9975, 14012, 26717, 24686, 22282, 29602, 20487, 14060, 11487, 11640, 27247, 17937, 12188, 26959, 8459, 23324, 17487, 6498, 12099, 2120, 22420, 5551, 3509, 4281, 29326, 27625, 12910, 14816, 16537, 13979, 27687, 15200, 19592, 15123, 12851, 29323, 30546, 31932, 12495, 25526, 26901, 11904, 21700, 8301, 12739, 11604, 12805, 21343, 31274, 3590, 11089, 9728, 24044, 1110, 30789, 21445, 17367, 12791, 27356, 26643, 7685, 14717, 23983, 23482, 223, 28151, 17735, 29071, 11631, 30091, 1844, 24248, 24452, 11384, 16674, 28266, 7530, 11551, 23704, 623, 17631, 3793, 25360, 18000, 23308, 20446, 23867, 17304, 11165, 10174, 4484, 925, 9199, 9800, 26485, 15050, 17498, 29724, 6691, 18871, 16308, 30167, 29545, 9424, 28865, 18024, 23357, 27364, 32607, 20749, 16082, 16470, 15824, 19470, 1122, 16289, 11445, 15427, 12303, 8280, 16260, 21593, 16582, 30978, 6061, 11674, 11022, 30252, 30658, 15398, 13373, 9448, 5266, 2309, 19566, 21737, 1191, 29023, 7764, 27011, 16062, 14728, 25888, 15701, 22080, 20470, 26520, 18383, 5007, 5904, 1641, 5296, 29506, 24278, 11125, 21934, 267, 31281, 23044, 28428, 21159, 3593, 29389, 31404, 21816, 32252, 8440, 4050, 4556, 31095, 16830, 13099, 20462, 472, 24826, 29698, 27329, 10587, 15091, 463, 15413, 5980, 7712, 10896, 27470, 8859, 30812, 18377, 5288, 24315, 16265, 27800, 19863, 6883, 13602, 25178, 24250, 13936, 21549, 16441, 17328, 4739, 31858, 25942, 18640, 14999, 3543, 4311, 15920, 14004, 18801, 25211, 32691, 1192, 23211, 11282, 32224, 30275, 23381, 29521, 32534, 21670, 13044, 4585, 28947, 25193, 17195, 25421, 23918, 8593, 11086, 15300, 22560, 15383, 6489, 25015, 28231, 25287, 4249, 26570, 6239, 19216, 20623, 3998, 18251, 2467, 1086, 20921, 20223, 5910, 30125, 1423, 9473, 21994, 17242, 18512, 28454, 10253, 8205, 32007, 23825, 2682, 18462, 27141, 4479, 15110, 25855, 20221, 32556, 11685, 1787, 10529, 27902, 8586, 32633, 28342, 26529, 16566, 12513, 18649, 12071, 23346, 20964, 32180, 7562, 24891, 12009, 11924, 23484, 5080, 7044, 18663, 1891, 19966, 25497, 8576, 17067, 9482, 1664, 10815, 20409, 17900, 14404, 20674, 32473, 32021, 11889, 7192, 27300, 27887, 20849, 23139, 26509, 26583, 183, 17044, 25085, 10148, 19757, 18657, 25375, 18404, 15563, 31045, 23719, 25833, 1347, 16203, 17146, 22054, 5078, 15500, 5559, 18575, 9775, 14044, 29535, 6201, 30970, 7408, 8397, 3639, 6808, 25661, 16986, 29083, 11344, 17563, 15777, 20387, 6722, 31618, 15933, 26974, 21516, 13154, 17178, 28631, 25206, 4445, 7630, 12348, 30582, 8134, 20737, 21326, 28837, 16424, 15329, 15537, 8632, 11399, 901, 24073, 15916, 24480, 23287, 20685, 22416, 20884, 19888, 23441, 3784, 5073, 9030, 16509, 26349, 30268, 32105, 21674, 14257, 16110, 11937, 25160, 25259, 13872, 30259, 8424, 29453, 13954, 6625, 2791, 18862, 10505, 14492, 22784, 14069, 25070, 13389, 13754, 10520 }; ================================================ FILE: SoA/VoxelUtils.h ================================================ #pragma once template inline T getXFromBlockIndex(T blockIndex) { return blockIndex & 0x1f; } template inline T getYFromBlockIndex(T blockIndex) { return blockIndex >> 10; } template inline T getZFromBlockIndex(T blockIndex) { return (blockIndex >> 5) & 0x1f; } template inline void getPosFromBlockIndex(T blockIndex, T& x, T& y, T& z) { x = blockIndex & 0x1f; y = blockIndex >> 10; z = (blockIndex >> 5) & 0x1f; } template inline void getPosFromBlockIndex(T blockIndex, glm::tvec3& pos) { pos.x = blockIndex & 0x1f; pos.y = blockIndex >> 10; pos.z = (blockIndex >> 5) & 0x1f; } template inline i32v3 getPosFromBlockIndex(T blockIndex) { return i32v3(blockIndex & 0x1f, blockIndex >> 10, (blockIndex >> 5) & 0x1f); } static_assert(CHUNK_WIDTH == 32, "getPosFromBlockIndex assumes 32 chunk width"); template inline int getBlockIndexFromPos(T x, T y, T z) { return x | (y << 10) | (z << 5); } template inline int getBlockIndexFromPos(const T& pos) { return pos.x | (pos.y << 10) | (pos.z << 5); } ================================================ FILE: SoA/VoxelVertices.h ================================================ /// /// VoxelVertices.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 24 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Vertex structs for different voxel types /// #pragma once #ifndef VoxelVertices_h__ #define VoxelVertices_h__ struct VertexVoxelChunkSolid { public: ui8v3 pos; ///< Position within its grid /// TODO: More }; #endif // VoxelVertices_h__ ================================================ FILE: SoA/WSO.cpp ================================================ #include "stdafx.h" #include "WSO.h" WSO::WSO(const WSOData* wsoData, const f64v3& pos) : position(pos), data(wsoData) { } WSO::~WSO() { // We Don't Have Any Allocated Resources Yet } ================================================ FILE: SoA/WSO.h ================================================ #pragma once class WSOData; #include class WSO { public: WSO(const WSOData* wsoData, const f64v3& pos); ~WSO(); const f64v3 position; const WSOData* const data; }; ================================================ FILE: SoA/WSOAtlas.cpp ================================================ #include "stdafx.h" #include "WSOAtlas.h" #include #include "WSOData.h" // This Information Is Found At The Beginning Of The WSO File class WSOFileHeader { public: // The Size Of DATA Segment i32 dataSegmentSize; // Amount Of WSO Index Information To Read i32 wsoCount; }; // This Is Information About A WSO class WSOIndexInformation { public: // Location In The File i32 fileOffset; // Amount Of Data To Read i32v3 size; // The Length Of The String Of The WSO's Name i32 lenName; // The Length Of The String Of The WSO's Model File (If Any) i32 lenModelFile; }; WSOAtlas::WSOAtlas() { // Guess What... I Don't Have To Do Anything } WSOAtlas::~WSOAtlas() { // Dang... Now I Have Work To Do clear(); } void WSOAtlas::add(WSOData* data) { // Set Its Index data->index = _data.size(); // Place In The Data _data.push_back(data); // Add By Name _mapName[data->name] = data; } void WSOAtlas::load(const cString file) { vio::IOManager iom; // Attempt To Open The File vfstream f = iom.openFile(file, vio::FileOpenFlags::READ_ONLY_EXISTING | vio::FileOpenFlags::BINARY); if (!f.isOpened()) return; // Read The Header WSOFileHeader header; f.read(1, sizeof(WSOFileHeader), &header); // Read All The Index Information WSOIndexInformation* indices = new WSOIndexInformation[header.wsoCount]; f.read(header.wsoCount, sizeof(WSOIndexInformation), indices); // Read The DATA Segment ubyte* data = new ubyte[header.dataSegmentSize]; f.read(header.dataSegmentSize, sizeof(ubyte), data); // Close The File f.close(); // Allocate Memory For All The WSO Data WSOData* wsoData = new WSOData[header.wsoCount](); _allocatedMem.push_back(wsoData); // Calculate Block Sizes For WSOs i32 nameBlockSize = 0, modelFileBlockSize = 0, idBlockSize = 0; for (i32 i = 0; i < header.wsoCount; i++) { nameBlockSize += indices[i].lenName; modelFileBlockSize += indices[i].lenModelFile; idBlockSize += indices[i].size.x * indices[i].size.y * indices[i].size.z; } // Allocate Memory For Names nameBlockSize += header.wsoCount; cString names = new char[nameBlockSize]; _allocatedMem.push_back(names); // Allocate Memory For Model Files modelFileBlockSize += header.wsoCount; cString modelFiles = new char[modelFileBlockSize]; _allocatedMem.push_back(modelFiles); // Allocate Memory For IDs i16* ids = new i16[idBlockSize]; _allocatedMem.push_back(ids); // Create All The WSOs for (i32 i = 0; i < header.wsoCount; i++) { // Find Location In DATA ubyte* wso = data + indices[i].fileOffset; wsoData[i].size = indices[i].size; // Copy Over The Name wsoData[i].name = names; memcpy(wsoData[i].name, wso, indices[i].lenName); wsoData[i].name[indices[i].lenName] = 0; names += indices[i].lenName + 1; // Move To Model File Portion wso += indices[i].lenName; // Copy Over The File (Otherwise Use Voxels As The Default) if (indices[i].lenModelFile > 0) { wsoData[i].modelFile = names; memcpy(wsoData[i].modelFile, wso, indices[i].lenModelFile); wsoData[i].modelFile[indices[i].lenModelFile] = 0; names += indices[i].lenModelFile + 1; // Move To ID Portion wso += indices[i].lenModelFile; } else { wsoData[i].modelFile = nullptr; } // Copy Over ID Information wsoData[i].wsoIDs = ids; i32 idSize = wsoData[i].getBlockCount() * sizeof(i16); memcpy(wsoData[i].wsoIDs, wso, idSize); ids += wsoData[i].getBlockCount(); // Add This Into The Atlas add(wsoData + i); } // Delete File Info delete[] indices; delete[] data; } void WSOAtlas::clear() { // Free All The Allocated Memory // for (i32 i = _allocatedMem.size() - 1; i >= 0; i--) { // delete[] _allocatedMem[i]; // } if(_allocatedMem.size() == 4) { delete (WSOData*)_allocatedMem[0]; delete (cString)_allocatedMem[1]; delete (cString)_allocatedMem[2]; delete (i16*)_allocatedMem[3]; } // Clear ADT Memory std::vector().swap(_data); std::map().swap(_mapName); std::vector().swap(_allocatedMem); } ================================================ FILE: SoA/WSOAtlas.h ================================================ #pragma once class WSOData; #include /************************************************************************/ /* WSO File Specification */ /* FileSpecs\WSO.txt */ /************************************************************************/ class WSOAtlas { public: WSOAtlas(); ~WSOAtlas(); // Add Data To Atlas (But Memory Is Not Associated With It) void add(WSOData* data); // Load WSO Data From A File void load(const cString file); // Destroy All Memory Associated With This Atlas void clear(); // The Number Of Data In The Atlas ui32 getSize() const { return _data.size(); } // Retrieve Data From The Atlas WSOData* get(const size_t& index) const { return _data[index]; } WSOData* operator[] (const size_t& index) const { return get(index); } WSOData* get(nString name) const { return _mapName.at(name); } WSOData* operator[] (nString name) const { return get(name); } private: // Pointers To All The Data std::vector _data; // Access Data By A Name std::map _mapName; // Pointers To Allocated Memory Blocks std::vector _allocatedMem; }; ================================================ FILE: SoA/WSOData.h ================================================ #pragma once const i16 WSO_DONT_CARE_ID = (i16)0xffff; #define WSO_MAX_SIZE 8 #define WSO_NAME_MAX_LENGTH 128 // Stores All Combinations Of A WSO class WSOData { public: // The Number Of Blocks Inside This WSO i32 getBlockCount() const { return size.x * size.y * size.z; } // The Name Of This WSO cString name; // The Index Of This WSO In The Atlas i32 index; // Necessary IDs For The WSO To Exist i16* wsoIDs; // The Size Of The WSO i32v3 size; // The Model File (If It Wants One) cString modelFile; }; ================================================ FILE: SoA/WSOScanner.cpp ================================================ #include "stdafx.h" #include "WSOScanner.h" #include "WSO.h" #include "WSOAtlas.h" #include "WSOData.h" #include "ChunkGrid.h" // Scan A Radius Of (WSO_MAX_SIZE - 1) From The Center Block const int WSO_QUERY_SIZE = WSO_MAX_SIZE * 2 - 1; WSOScanner::WSOScanner(WSOAtlas* atlas VORB_UNUSED) //: //_wsoAtlas(atlas) { // TODO: Revisit this. } bool checkForWSO(const i16* query, const WSOData* data, i32v3& offset) { i32v3 minPos(WSO_MAX_SIZE); minPos -= data->size; i32v3 localOff; // TODO: Check Y-Rotations // Loop Through Offsets for (offset.y = minPos.y; offset.y < WSO_MAX_SIZE; offset.y++) { for (offset.z = minPos.z; offset.z < WSO_MAX_SIZE; offset.z++) { for (offset.x = minPos.x; offset.x < WSO_MAX_SIZE; offset.x++) { // Try To Find A WSO At This Offset i32v3 maxPos(offset + data->size); bool isOK = true; i32 dataIndex = 0; for (localOff.y = offset.y; localOff.y < maxPos.y && isOK; localOff.y++) { for (localOff.z = offset.z; localOff.z < maxPos.z && isOK; localOff.z++) { for (localOff.x = offset.x; localOff.x < maxPos.x && isOK; localOff.x++) { if (data->wsoIDs[dataIndex] != WSO_DONT_CARE_ID) { i32 qIndex = (localOff.y * WSO_QUERY_SIZE + localOff.z) * WSO_QUERY_SIZE + localOff.x; i16 id = query[qIndex]; if (data->wsoIDs[dataIndex] != id) isOK = false; else printf("I Found One Charlie %d\n", dataIndex); } else printf("I Don't Care Charlie %d\n", dataIndex); dataIndex++; } } } // All Requirements Of WSO Are Met if (isOK) return true; } } } // Could Not Find A Single One return false; } std::vector WSOScanner::scanWSOs(const i32v3& position VORB_UNUSED, ChunkGrid* cg VORB_UNUSED) { // TODO: Fix this and remove unused tags. //// Get A Piece Of The World //const i16* query = getQuery(position, cg); //std::vector wsos; //// Loop Through All Possible WSOs //i32v3 offset; //for (i32 i = _wsoAtlas->getSize() - 1; i >= 0; i--) { // WSOData* data = _wsoAtlas->get(i); // if (checkForWSO(query, data, offset)) { // i32v3 localPos = offset - i32v3(WSO_MAX_SIZE - 1); // localPos += position; // // This Is A Possible WSO // //TODO(Cristian) Make this work for new chunkmanager mapping // //WSO* wso = new WSO(data, f64v3(localPos + cm->cornerPosition)); // //wsos.push_back(wso); // } //} //// TODO: Make Sure We Don't Get Already Created WSOs //delete query; //return wsos; return std::vector(); } const i16* WSOScanner::getQuery(const i32v3& position VORB_UNUSED, ChunkGrid* cg VORB_UNUSED) { // TODO: Fix this and remove tags. //// Get The Query Based Off Of The Max Size Of The WSO //const i32v3 minPos = position - i32v3(WSO_MAX_SIZE - 1); //const i32v3 maxPos = position + i32v3(WSO_MAX_SIZE - 1); //return cg->getIDQuery(minPos, maxPos); return nullptr; } ================================================ FILE: SoA/WSOScanner.h ================================================ #pragma once #include class ChunkGrid; class WSO; class WSOAtlas; // A Scanner That Uses An Atlas Of Known WSOs To Attempt Find WSOs class WSOScanner { public: // A Scanner Begins Its Life Knowing About A Certain Atlas WSOScanner(WSOAtlas* atlas); // Retrieve All The WSOs std::vector scanWSOs(const i32v3& position, ChunkGrid* cm); private: // Obtain A Box Volume Of Voxel IDs const i16* getQuery(const i32v3& position, ChunkGrid* cm); // This Does Not Have To Point To A Global Atlas Necessarily ;) // WSOAtlas* _wsoAtlas; }; ================================================ FILE: SoA/WorldIO.cpp ================================================ #include "stdafx.h" #include "WorldIO.h" #include //for mkdir windows #include #include #include #include #include #include #include "BlockData.h" #include "Chunk.h" #include "Errors.h" #include "FileSystem.h" #include "GameManager.h" #include "Options.h" #include "Planet.h" #include "Player.h" //#define EXTRACTINT(a, i) ( (((GLuint)((GLubyte *)(a))[(i)]) << 24 ) | (((GLuint)((GLubyte *)(a))[(i)+1]) << 16) | (((GLuint)((GLubyte *)(a))[(i)+2]) << 8) | ((GLuint)((GLubyte *)(a))[(i)+3]) ) void ThreadError(string msg){ cout << msg << endl; pError(msg.c_str()); } WorldIO::WorldIO() { _maxLocationBufferCacheSize = 15; _currReg = ""; _isThreadFinished = 0; _isDirtyLocationBuffer = 0; _currLocationBuffer = NULL; readWriteThread = NULL; _threadFile = NULL; _shouldDisableLoading = 0; } WorldIO::~WorldIO() { onQuit(); } void WorldIO::compressBlockData(Chunk *ch) { GLushort *blockData = ch->data; GLubyte *lightData = ch->lightData[0]; _bufferSize = 0; GLushort curr; GLubyte currb; GLuint count = 1; int c; int jStart, jEnd, jInc; int kStart, kEnd, kInc; int jMult, kMult; switch(ch->faceData.rotation){ //we use rotation value to un-rotate the chunk data case 0: //no rotation jStart = 0; kStart = 0; jEnd = kEnd = CHUNK_WIDTH; jInc = kInc = 1; jMult = CHUNK_WIDTH; kMult = 1; break; case 1: //up is right jMult = 1; jStart = CHUNK_WIDTH-1; jEnd = -1; jInc = -1; kStart = 0; kEnd = CHUNK_WIDTH; kInc = 1; kMult = CHUNK_WIDTH; break; case 2: //up is down jMult = CHUNK_WIDTH; jStart = CHUNK_WIDTH-1; kStart = CHUNK_WIDTH-1; jEnd = kEnd = -1; jInc = kInc = -1; kMult = 1; break; case 3: //up is left jMult = 1; jStart = 0; jEnd = CHUNK_WIDTH; jInc = 1; kMult = CHUNK_WIDTH; kStart = CHUNK_WIDTH-1; kEnd = -1; kInc = -1; break; } curr = blockData[jStart*jMult + kStart*kMult]; bool first = 1; //compress and store block ID data for (int i = 0; i < CHUNK_WIDTH; i++){ //y for (int j = jStart; j != jEnd; j+=jInc){ //z for (int k = kStart; k != kEnd; k+=kInc){ //x if (!first){ //have to ignore the first one since we set it above c = i*CHUNK_LAYER + j*jMult + k*kMult; //sometimes x is treated like z and visa versa when rotating if (blockData[c] != curr){ _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00) >> 8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = (GLubyte)((curr & 0xFF00) >> 8); _byteBuffer[_bufferSize++] = (GLubyte)(curr & 0xFF); curr = blockData[c]; count = 1; }else{ count++; } }else{ first = 0; } } } } _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00) >> 8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = (GLubyte)((curr & 0xFF00) >> 8); _byteBuffer[_bufferSize++] = (GLubyte)(curr & 0xFF); //compress and store artificial light count = 1; currb = lightData[jStart*jMult + kStart*kMult]; first = 1; for (int i = 0; i < CHUNK_WIDTH; i++){ //y for (int j = jStart; j != jEnd; j+=jInc){ //z for (int k = kStart; k != kEnd; k+=kInc){ //x if (!first){ //have to ignore the first one since we set it above c = i*CHUNK_LAYER + j*jMult + k*kMult; if (lightData[c] != currb){ //remove the count ==??? _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00)>>8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = currb; currb = lightData[c]; count = 1; }else{ count++; } }else{ first = 0; } } } } _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00)>>8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = currb; //compress and store voxel sunlight count = 1; currb = lightData[CHUNK_SIZE + jStart*jMult + kStart*kMult]; first = 1; for (int i = 0; i < CHUNK_WIDTH; i++){ //y for (int j = jStart; j != jEnd; j+=jInc){ //z for (int k = kStart; k != kEnd; k+=kInc){ //x if (!first){ //have to ignore the first one since we set it above c = i*CHUNK_LAYER + j*jMult + k*kMult; if (lightData[CHUNK_SIZE + c] != currb){ //remove the count ==??? _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00)>>8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = currb; currb = lightData[CHUNK_SIZE + c]; count = 1; }else{ count++; } }else{ first = 0; } } } } _byteBuffer[_bufferSize++] = (GLubyte)((count & 0xFF00)>>8); _byteBuffer[_bufferSize++] = (GLubyte)(count & 0xFF); _byteBuffer[_bufferSize++] = currb; for (int j = jStart; j != jEnd; j+=jInc){ //z for (int k = kStart; k != kEnd; k+=kInc){ //x c = j*jMult + k*kMult; _byteBuffer[_bufferSize++] = ((GLubyte *)ch->sunLight)[c]; } } if (_bufferSize >= 524288){ cout << "ITS THE BUFFER SIZE DAMMIT "; cout << _bufferSize << endl; int a; cin >> a; } _compressedSize = CRW_BYTE_BUFSIZE + 16 + 52430; int zresult = compress2(&(_compressedByteBuffer[4]), &_compressedSize, _byteBuffer, _bufferSize, 6); //set the size bytes _compressedByteBuffer[0] = (GLubyte)((_compressedSize & 0xFF000000) >> 24); _compressedByteBuffer[1] = (GLubyte)((_compressedSize & 0x00FF0000) >> 16); _compressedByteBuffer[2] = (GLubyte)((_compressedSize & 0x0000FF00) >> 8); _compressedByteBuffer[3] = (GLubyte)(_compressedSize & 0x000000FF); _compressedSize += 4; //add size of size bytes switch( zresult ) { case Z_MEM_ERROR: ThreadError("zlib compression: out of memory\n"); exit(1); // quit. case Z_BUF_ERROR: ThreadError("zlib compression: output buffer wasn't large enough\n"); exit(1); // quit. } } int WorldIO::saveToFile(Chunk *ch) { GLuint offset; GLuint bufSize = 0; GLubyte *endDataBuffer = NULL; int sizeDiff = 0; GLuint padLength; GLuint endDataBufferSize = 0; compressBlockData(ch); _chunkLength = _compressedSize; //size of our new chunk padLength = SECTOR_SIZE - _chunkLength%SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; _chunkLength += padLength; //pad the length //try to seek to location seekToChunkOffset(ch); GLuint num; GLuint newBlockSize = _chunkLength/SECTOR_SIZE; if (newBlockSize > _chunkBlockSize || newBlockSize < _chunkBlockSize) // if we need more or fewer blocks { sizeDiff = newBlockSize - _chunkBlockSize; bufSize = _fileSize/SECTOR_SIZE - (_chunkOffset + _chunkBlockSize); //number of blocks to copy if (bufSize != 0){ //check if there is stuff after us that will be displaced endDataBuffer = new GLubyte[bufSize*SECTOR_SIZE]; //for storing all the data that will need to be copied in the end endDataBufferSize = bufSize*SECTOR_SIZE; if (fseek(_threadFile, _fileSize - bufSize*SECTOR_SIZE, SEEK_SET) != 0){ cout << "Region: Chunk data fseek C error! " << _fileSize << " " << bufSize << " " << _fileSize - bufSize*SECTOR_SIZE << endl; } num = 8192; for (GLuint i = 0; i < endDataBufferSize; i+= 8192){ if (endDataBufferSize - i < num){ padLength = SECTOR_SIZE - (endDataBufferSize-i)%SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = endDataBufferSize-i + padLength; } if (fread(&(endDataBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not read enough bytes at A\n"; } } } } if (fseek(_threadFile, _chunkOffset*SECTOR_SIZE, SEEK_SET) != 0){ cout << "Region: Chunk data fseek D error! " << _fileSize << " " << _chunkOffset << " " << _chunkOffset*SECTOR_SIZE << endl; int a; cin >> a; } //write data num = 8192; for (GLuint i = 0; i < _compressedSize; i += 8192){ if (_compressedSize - i < num){ padLength = SECTOR_SIZE - (_compressedSize-i)%SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = _compressedSize-i + padLength; } if (fwrite(&(_compressedByteBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not write enough bytes at A\n"; } } //write leftover data num = 8192; for (GLuint i = 0; i < endDataBufferSize; i+= 8192){ if (endDataBufferSize - i < num){ padLength = SECTOR_SIZE - (endDataBufferSize-i)%SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = endDataBufferSize-i + padLength; } if (fwrite(&(endDataBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not write enough bytes at B\n"; } } if (sizeDiff < 0){ //if the file got smaller if (truncate(_fileSize + sizeDiff*SECTOR_SIZE) != 0){ //truncate the size cout << "Region: Truncate error!\n"; perror(" region file "); } } _fileSize = _fileSize + sizeDiff*SECTOR_SIZE; GLuint location; location = extractInt(_currLocationBuffer->buffer, _locOffset); offset = (location >> 8); if (offset != _chunkOffset){ _currLocationBuffer->buffer[_locOffset] = (GLubyte)((_chunkOffset & 0xFF0000) >> 16); _currLocationBuffer->buffer[_locOffset + 1] = (GLubyte)((_chunkOffset & 0xFF00) >> 8); _currLocationBuffer->buffer[_locOffset + 2] = (GLubyte)(_chunkOffset & 0xFF); _isDirtyLocationBuffer = 1; } if (_chunkBlockSize != newBlockSize){ _currLocationBuffer->buffer[_locOffset + 3] = (GLubyte)newBlockSize; //set the blockSize _isDirtyLocationBuffer = 1; } //update the table if (bufSize != 0){ for (int i = 0; i < 16384; i+=4){ location = extractInt(_currLocationBuffer->buffer, i); offset = (location >> 8); if (offset > _chunkOffset){ //if its an in use chunk offset += sizeDiff; _currLocationBuffer->buffer[i] = (GLubyte)((offset & 0xFF0000) >> 16); _currLocationBuffer->buffer[i + 1] = (GLubyte)((offset & 0xFF00) >> 8); _currLocationBuffer->buffer[i + 2] = (GLubyte)(offset & 0xFF); _isDirtyLocationBuffer = 1; } } } if (_isDirtyLocationBuffer){ fseek(_threadFile, 0, SEEK_SET); //go back to beginning of file to save the table fwrite(_currLocationBuffer->buffer, 1, 16384, _threadFile); _isDirtyLocationBuffer = 0; } fflush(_threadFile); if (endDataBuffer){ delete[] endDataBuffer; //no longer need the buffer } return 0; } int WorldIO::loadFromFile(Chunk *ch) { _locOffset = getLocOffset(ch); GLuint location = extractInt(_currLocationBuffer->buffer, _locOffset); _chunkOffset = (location >> 8);//grab the 3 offset bytes _chunkBlockSize = (location & 0xFF);//grab the block size byte if (_chunkOffset == 0){ //Error(("TRIED TO LOAD CHUNK WITH NO OFFSET " + to_string(locOffset) + " " + to_string(location) + " " + currReg).c_str()); cout << "Nonfatal error: TRIED TO LOAD CHUNK WITH NO OFFSET " + to_string(_locOffset) + " " + to_string(location) + " " + _currReg << endl; return 1; } if (fseek(_threadFile, _chunkOffset*SECTOR_SIZE, SEEK_SET) != 0){ cout << "Region: Chunk data fseek C error! " << _fileSize << " " << _chunkOffset << " " << _chunkOffset*SECTOR_SIZE << " " << _threadFile << endl; return 1; } int num = 8192; int padLength; int size = _chunkBlockSize*SECTOR_SIZE; if (size >= 262144)pError("Region input Byte Buffer overflow"); for (int i = 0; i < size; i+=8192){ if (size - i < num){ padLength = SECTOR_SIZE - (size-i)%SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = size-i + padLength; } if (fread(&(_compressedByteBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not read enough bytes at Z\n"; return 1; } } _bufferSize = CRW_BYTE_BUFSIZE; _compressedSize = extractInt(_compressedByteBuffer, 0); //grab the size int int zresult = uncompress(_byteBuffer, &_bufferSize, &(_compressedByteBuffer[4]), _compressedSize); switch( zresult ) { case Z_MEM_ERROR: ThreadError("zlib uncompression: out of memory\n"); exit(1); // quit. case Z_BUF_ERROR: ThreadError("zlib uncompression: output buffer wasn't large enough\n"); exit(1); // quit. } unsigned long b = 0; int step = 0;//0 = data, 1 = lightdata //cout << "READSIZE " << size << endl; GLuint bindex = 0; GLushort blockID; GLushort runSize; GLubyte lightVal; int c; int jStart, jEnd, jInc; int kStart, kEnd, kInc; int jMult, kMult; switch(ch->faceData.rotation){ //we use rotation value to un-rotate the chunk data case 0: //no rotation jStart = 0; kStart = 0; jEnd = kEnd = CHUNK_WIDTH; jInc = kInc = 1; jMult = CHUNK_WIDTH; kMult = 1; break; case 1: //up is right jMult = 1; jStart = CHUNK_WIDTH-1; jEnd = -1; jInc = -1; kStart = 0; kEnd = CHUNK_WIDTH; kInc = 1; kMult = CHUNK_WIDTH; break; case 2: //up is down jMult = CHUNK_WIDTH; jStart = CHUNK_WIDTH-1; kStart = CHUNK_WIDTH-1; jEnd = kEnd = -1; jInc = kInc = -1; kMult = 1; break; case 3: //up is left jMult = 1; jStart = 0; jEnd = CHUNK_WIDTH; jInc = 1; kMult = CHUNK_WIDTH; kStart = CHUNK_WIDTH-1; kEnd = -1; kInc = -1; break; default: cout << "ERROR Chunk Loading: Rotation value not 0-3"; int a; cin >> a; return 1; break; } int i = 0; //y int j = jStart; //z int k = kStart; //x int sunLightAdd = 0; ch->num = 0; while (b < _bufferSize){ //blockData if (step == 0){ if (b >= 524280) { cout << "ERROR: Chunk File Corrupted! :( " << _bufferSize << " " << bindex << " " << _locOffset << endl; return 1; } runSize = (((GLushort)_byteBuffer[b]) << 8) | ((GLushort)_byteBuffer[b+1]); blockID = (((GLushort)_byteBuffer[b+2]) << 8) | ((GLushort)_byteBuffer[b+3]); if (blockID != 0) ch->num += runSize; for (int q = 0; q < runSize; q++){ c = i*CHUNK_LAYER + j*jMult + k*kMult; if (c >= CHUNK_SIZE){ cout << "Chunk File Corrupted!\n"; return 1; } ch->data[c] = blockID; if (GETBLOCK(ch->data[c]).spawnerVal || GETBLOCK(ch->data[c]).sinkVal){ ch->activeBlocks.push_back(c); } if (blockID >= LOWWATER && blockID <= FULLWATER) ch->hasWater = 1; bindex++; k += kInc; if (k == kEnd){ k = kStart; j += jInc; if (j == jEnd){ j = jStart; i++; } } } if (bindex == CHUNK_SIZE){ i = 0; j = jStart; k = kStart; step = 1; bindex = 0; } b += 4; }else if (step == 1){ //lightData runSize = (((GLushort)_byteBuffer[b]) << 8) | ((GLushort)_byteBuffer[b+1]); lightVal = _byteBuffer[b+2]; for (int q = 0; q < runSize; q++){ c = i*CHUNK_LAYER + j*jMult + k*kMult; if (sunLightAdd + c >= CHUNK_SIZE * 2){ cout << "Corruption when filling light data from loaded chunk."; return 1; } ((GLubyte *)(ch->lightData))[sunLightAdd+c] = lightVal; bindex++; k += kInc; if (k == kEnd){ k = kStart; j += jInc; if (j == jEnd){ j = jStart; i++; } } if (bindex == CHUNK_SIZE){ //start over for sunLight i = 0; j = jStart; k = kStart; sunLightAdd = CHUNK_SIZE; } } if (bindex == CHUNK_SIZE*2){ step = 2; bindex = 0; } b += 3; }else{ //sunlights data c = j*jMult + k*kMult; if (c >= 1024){ cout << "Chunk File Corrupted!\n"; return 1; } ch->sunLight[c] = ((GLbyte *)_byteBuffer)[b]; bindex++; k += kInc; if (k == kEnd){ k = kStart; j += jInc; } b++; // if (bindex == 1024){ // break; // } } } if (bindex != 1024){ cout << "ERROR Chunk Loading: bindex went too far! " << bindex << " " << step << endl; return 1; } return 0; } int WorldIO::deleteChunkFile(Chunk *ch) { _locOffset = getLocOffset(ch); GLubyte *endDataBuffer = NULL; GLuint endDataBufferSize = 0; GLuint num; int padLength; GLuint location = extractInt(_currLocationBuffer->buffer, _locOffset); _chunkOffset = (location >> 8);//grab the 3 offset bytes _chunkBlockSize = (location & 0xFF);//grab the block size byte //clear the spot in the lookup table setInt(_currLocationBuffer->buffer, _locOffset, 0); _isDirtyLocationBuffer = 1; int sizeDiff = -((int)_chunkBlockSize); int bufSize = _fileSize / SECTOR_SIZE - (_chunkOffset + _chunkBlockSize); //number of blocks to copy if (bufSize < 0) { cout << "Save file is corrupted! :( Attempting to recover... but your save may be ruined. I am sorry..." << endl; bufSize = 0; } if (bufSize != 0){ //check if there is stuff after us that will be displaced endDataBuffer = new GLubyte[bufSize*SECTOR_SIZE]; //for storing all the data that will need to be copied in the end endDataBufferSize = bufSize*SECTOR_SIZE; if (fseek(_threadFile, _fileSize - bufSize*SECTOR_SIZE, SEEK_SET) != 0){ cout << "Region: Chunk data fseek C error! " << _fileSize << " " << bufSize << " " << _fileSize - bufSize*SECTOR_SIZE << endl; int a; cin >> a; } num = 8192; for (GLuint i = 0; i < endDataBufferSize; i += 8192){ if (endDataBufferSize - i < num){ padLength = SECTOR_SIZE - (endDataBufferSize - i) % SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = endDataBufferSize - i + padLength; } if (fread(&(endDataBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not read enough bytes at A delete\n"; } } } //this is the same as save? if (fseek(_threadFile, _chunkOffset*SECTOR_SIZE, SEEK_SET) != 0){ cout << "Region: Chunk data fseek D error! " << _fileSize << " " << _chunkOffset << " " << _chunkOffset*SECTOR_SIZE << endl; int a; cin >> a; } //write leftover data num = 8192; for (GLuint i = 0; i < endDataBufferSize; i += 8192){ if (endDataBufferSize - i < num){ padLength = SECTOR_SIZE - (endDataBufferSize - i) % SECTOR_SIZE; if (padLength == SECTOR_SIZE) padLength = 0; num = endDataBufferSize - i + padLength; } if (fwrite(&(endDataBuffer[i]), 1, num, _threadFile) != num){ cout << "Did not write enough bytes at B delete\n"; } } if (sizeDiff < 0){ //if the file got smaller if (truncate(_fileSize + sizeDiff*SECTOR_SIZE) != 0){ //truncate the size cout << "Region: Truncate error!\n"; perror(" region file "); int a; cin >> a; } } if (_isDirtyLocationBuffer){ fseek(_threadFile, 0, SEEK_SET); //go back to beginning of file to save the table fwrite(_currLocationBuffer->buffer, 1, 16384, _threadFile); _isDirtyLocationBuffer = 0; } fflush(_threadFile); return 0; } int WorldIO::tryReadFromFile() { FILE *f; string filePath = "Saves/Save1/test.soas"; f = fopen(filePath.c_str(), "rb"); if (f == NULL){ pError("Could not open test.soas for reading"); return -1; } fclose(f); return 0; } void WorldIO::addToSaveList(Chunk *ch) { if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->dirty = 0; string rs = getRegionString(ch); _queueLock.lock(); ch->inSaveThread = 1; chunksToSave.push(ch); _queueLock.unlock(); _cond.notify_one(); } } void WorldIO::addToSaveList(vector &chunks) { _queueLock.lock(); Chunk *ch; for (size_t i = 0; i < chunks.size(); i++){ ch = chunks[i]; if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->inSaveThread = 1; ch->dirty = 0; chunksToSave.push(ch); } } _queueLock.unlock(); _cond.notify_one(); } void WorldIO::addToLoadList(Chunk *ch) { if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ _queueLock.lock(); ch->loadStatus = 0; ch->inLoadThread = 1; chunksToLoad.push(ch); _queueLock.unlock(); _cond.notify_one(); } } void WorldIO::addToLoadList(vector &chunks) { _queueLock.lock(); Chunk *ch; if (_shouldDisableLoading) { flcLock.lock(); for (size_t i = 0; i < chunks.size(); i++){ chunks[i]->loadStatus = 2; finishedLoadChunks.push_back(chunks[i]); } flcLock.unlock(); _queueLock.unlock(); return; } for (size_t i = 0; i < chunks.size(); i++){ ch = chunks[i]; if (ch->inSaveThread == 0 && ch->inLoadThread == 0){ ch->inLoadThread = 1; chunksToLoad.push(ch); } else{ cout << "ERROR: Tried to add chunk to load list and its in a thread! : " << ch->position.x << " " << ch->position.y << " " << ch->position.z << endl; } } _queueLock.unlock(); _cond.notify_one(); } string WorldIO::getRegionString(Chunk *ch) { int rot = ch->faceData.rotation; int face = ch->faceData.face; int idir = FaceOffsets[face][rot][0]; int jdir = FaceOffsets[face][rot][1]; int ip = (ch->faceData.ipos - GameManager::planet->radius/CHUNK_WIDTH)*idir; int jp = (ch->faceData.jpos - GameManager::planet->radius/CHUNK_WIDTH)*jdir; if (rot%2){ //when rot%2 i and j must switch return "r." + to_string(ip >> 4) + "." + to_string((int)floor(ch->position.y / 32.0f) >> 4) + "." + to_string(jp >> 4); }else{ return "r." + to_string(jp >> 4) + "." + to_string((int)floor(ch->position.y / 32.0f) >> 4) + "." + to_string(ip >> 4); } } int WorldIO::openRegionFile(string reg, int face, bool create, FILE **file, int &fd) { string filePath; struct stat statbuf; if (*file != NULL){ if (reg != _currReg){ if (_isDirtyLocationBuffer){ if (fseek(*file, 0, SEEK_SET) != 0){ //go back to beginning of file to save the table ThreadError("Fseek error G could not seek to start\n"); } if (fwrite(_currLocationBuffer->buffer, 1, 16384, *file) != 16384){ ThreadError("Region write error G could not write loc buffer\n"); } _isDirtyLocationBuffer = 0; } fclose(*file); *file = NULL; }else{ return 0; //the file is already open } } _currReg = reg; _isDirtyLocationBuffer = 0; //Check to see if the save file has filePath = saveFilePath + "/Region/f" + to_string(face) + "/" + _currReg + ".soar"; // cout << filePath << endl; (*file) = fopen(filePath.c_str(), "rb+"); //open file if it exists if (*file == NULL){ if (create){ (*file) = fopen(filePath.c_str(), "wb+"); //create the file if (*file == NULL){ return 1; } }else{ return 1 ; } } fd = fileno(*file); //get file descriptor for truncate if needed if (fstat(fd, &statbuf) != 0) ThreadError("Stat call failed for region file open"); //get the file stats _fileSize = statbuf.st_size; if (_fileSize % SECTOR_SIZE){ ThreadError((filePath + ": Save File size must be multiple of " + to_string(SECTOR_SIZE) + ". Remainder = " + to_string(_fileSize%SECTOR_SIZE))); } if (_fileSize == 0){ //set up the initial location data _fileSize = 16384; _iterLocationCache = _locationCache.find(_currReg); if (_iterLocationCache != _locationCache.end()){ _currLocationBuffer = _iterLocationCache->second; }else{ _currLocationBuffer = newLocationBuffer(); } //initialize to 0 memset(_currLocationBuffer->buffer, 0, 16384); if (fwrite(_currLocationBuffer->buffer, 1, 16384, *file) != 16384){ ThreadError("Region write error F could not write loc buffer\n"); } fflush(*file); }else{ //load inital location data into memory _iterLocationCache = _locationCache.find(_currReg); if (_iterLocationCache != _locationCache.end()){ _currLocationBuffer = _iterLocationCache->second; }else{ _currLocationBuffer = newLocationBuffer(); if (fseek(*file, 0, SEEK_SET) != 0){ ThreadError("Region fseek error F could not seek to start\n"); } if (fread(_currLocationBuffer->buffer, 1, 16384, *file) != 16384){ //read the whole buffer in ThreadError("Region read error H could not read locbuffer\n"); } } } return 0; } int WorldIO::isChunkSaved(Chunk *ch) { //cout << "RS:" << reg << " "; GLuint sLocOffset = getLocOffset(ch); int location = extractInt(_currLocationBuffer->buffer, sLocOffset); if (location != 0){ return 1; } return 0; } int WorldIO::seekToChunkOffset(Chunk *ch) { GLuint location; GLuint offset; _locOffset = getLocOffset(ch); location = extractInt(_currLocationBuffer->buffer, _locOffset); offset = (location >> 8); if (offset == 0){ //check to see if the 4 bytes are empty//endianness? _chunkOffset = _fileSize/SECTOR_SIZE; //if its empty, set our offset to the end of the file. _chunkBlockSize = 0; //it has no size }else{ _chunkOffset = offset; //grab the 3 offset bytes _chunkBlockSize = _currLocationBuffer->buffer[_locOffset + 3]; //grab the block size byte } if (fseek(_threadFile, _chunkOffset*SECTOR_SIZE, SEEK_SET) != 0){ //seek to the chunk location cout << "Region: Chunk data fseek B error! " << _fileSize << " " << _chunkOffset << " " << _chunkOffset*SECTOR_SIZE << " " << _threadFile << endl; int a; cin >> a; } return 0; } void WorldIO::closeFile() { if (_threadFile != NULL) fclose(_threadFile); _threadFile = NULL; } i32 WorldIO::truncate(i64 size) { #if defined(_WIN32) || defined(_WIN64) return _chsize(_threadFileDescriptors, size); #else #ifdef POSIX return ftruncate(fd, size); #else // code for other OSes #endif #endif } GLuint WorldIO::getLocOffset(Chunk *ch) { int idir = FaceOffsets[ch->faceData.face][ch->faceData.rotation][0]; int jdir = FaceOffsets[ch->faceData.face][ch->faceData.rotation][1]; int ip = (ch->faceData.ipos - GameManager::planet->radius/CHUNK_WIDTH)*idir; int jp = (ch->faceData.jpos - GameManager::planet->radius/CHUNK_WIDTH)*jdir; if (ch->faceData.rotation%2){ //when rot%2 i and j must switch int tmp = ip; ip = jp; jp = tmp; } int ym = ((int)floor(ch->position.y / (float)CHUNK_WIDTH) % 16); int im = ip % 16; int jm = jp % 16; if (ym < 0) ym += 16;//modulus is weird in c++ for negative numbers if (im < 0) im += 16; if (jm < 0) jm += 16; GLuint lc = 4*(jm + im * 16 + ym * 256); if (lc >= 16384){ cout << "WRONG LOC OFFSET " << jm << " " << (im) * 16 << " " << ym * 256 << " " << lc << endl; int a; cin >> a; } return lc; } LocationBuffer *WorldIO::newLocationBuffer() { LocationBuffer *locBuf; if (_locationCacheQueue.size() >= _maxLocationBufferCacheSize){ locBuf = _locationCacheQueue.front(); _locationCacheQueue.pop(); auto i = _locationCache.find(locBuf->reg); if (i != _locationCache.end()){ _locationCache.erase(locBuf->reg); delete locBuf; } } locBuf = new LocationBuffer; locBuf->reg = _currReg; //maybe reg should be parameter _locationCache.insert(make_pair(locBuf->reg, locBuf)); _locationCacheQueue.push(locBuf); return locBuf; } void WorldIO::clearLoadList() { _queueLock.lock(); queue().swap(chunksToLoad); //clear the queue _queueLock.unlock(); } int WorldIO::getLoadListSize() { return chunksToLoad.size(); } int WorldIO::getSaveListSize() { int rv; _queueLock.lock(); rv = chunksToSave.size(); _queueLock.unlock(); return rv; } void WorldIO::readWriteChunks() { unique_lock ulock(_queueLock); Chunk *ch; bool failed; string reg; while (!_isDone){ if (_isDone){ ulock.unlock(); _isThreadFinished = 1; return; } _cond.wait(ulock); //wait for a notification that queue is not empty if (_isDone){ ulock.unlock(); _isThreadFinished = 1; return; } while (chunksToLoad.size() || chunksToSave.size()){ //loops through the load and save queues if (chunksToLoad.size()){ //do load list first ch = chunksToLoad.front(); chunksToLoad.pop(); ulock.unlock(); reg = getRegionString(ch); if ((openRegionFile(reg, ch->faceData.face, 0, &_threadFile, _threadFileDescriptors) == 0) && isChunkSaved(ch)){ failed = loadFromFile(ch); if (failed){ ch->loadStatus = 1; deleteChunkFile(ch); } } else{ ch->loadStatus = 2; //it isn't saved, so main thread will give it to the generate list. } flcLock.lock(); finishedLoadChunks.push_back(ch); flcLock.unlock(); ulock.lock(); } else if (chunksToSave.size()){ ch = chunksToSave.front(); ulock.unlock(); reg = getRegionString(ch); if (openRegionFile(reg, ch->faceData.face, 1, &_threadFile, _threadFileDescriptors)){ ThreadError("OPEN REG ERROR 2\n"); }; saveToFile(ch); //dont always do this? if (_isDirtyLocationBuffer){ fseek(_threadFile, 0, SEEK_SET); //go back to beginning of file to save the table fwrite(_currLocationBuffer->buffer, 1, 16384, _threadFile); _isDirtyLocationBuffer = 0; } ulock.lock(); chunksToSave.pop(); ch->inSaveThread = 0; //race condition! make a new queue! } } } } void WorldIO::beginThread() { _isDone = 0; _currReg = ""; _isThreadFinished = 0; _isDirtyLocationBuffer = 0; _currLocationBuffer = NULL; readWriteThread = NULL; _threadFile = NULL; readWriteThread = new std::thread(&WorldIO::readWriteChunks, this); } void WorldIO::onQuit() { clearLoadList(); while (getSaveListSize() != 0); _queueLock.lock(); _isDone = 1; _queueLock.unlock(); _cond.notify_one(); if (readWriteThread != NULL && readWriteThread->joinable()) readWriteThread->join(); delete readWriteThread; readWriteThread = NULL; if (_threadFile != NULL) fclose(_threadFile); _currLocationBuffer = NULL; _threadFile = NULL; _isDirtyLocationBuffer = 0; while (_locationCacheQueue.size()){ delete _locationCacheQueue.front(); _locationCacheQueue.pop(); } _locationCache.clear(); finishedLoadChunks.clear(); } ================================================ FILE: SoA/WorldIO.h ================================================ #pragma once #include #include #include #include #include #include #include class Chunk; const i32 SECTOR_SIZE = 512; struct LocationBuffer { public: ui8 buffer[16384]; nString reg; }; class WorldIO { public: WorldIO(); ~WorldIO(); void addToSaveList(Chunk* ch); void addToSaveList(std::vector& chunks); void addToLoadList(Chunk* ch); void addToLoadList(std::vector& chunks); void beginThread(); i32 isChunkSaved(Chunk* ch); void closeFile(); void clearLoadList(); i32 getLoadListSize(); i32 getSaveListSize(); void onQuit(); void setDisableLoading(bool disableLoading) { _shouldDisableLoading = disableLoading; } std::queue chunksToLoad; std::queue chunksToSave; //store by region string std::thread* readWriteThread; std::mutex flcLock; std::vector finishedLoadChunks; private: i32 saveToFile(Chunk* ch); i32 loadFromFile(Chunk* ch); i32 deleteChunkFile(Chunk* ch); i32 fillChunkData(Chunk* ch); i32 tryReadFromFile(); i32 openRegionFile(nString reg, i32 face, bool create, FILE** file, i32& fd); nString getRegionString(Chunk* ch); void compressBlockData(Chunk* ch); i32 seekToChunkOffset(Chunk* ch); i32 truncate(i64 size); ui32 getLocOffset(Chunk* ch); LocationBuffer* newLocationBuffer(); void readWriteChunks(); //used by the thread std::mutex _queueLock; std::condition_variable _cond; FILE* _threadFile; i32 _threadFileDescriptors; nString _currReg; #define CRW_BYTE_BUFSIZE 524288 //These two buffers store the voxel data for block IDs and voxel light ui8 _byteBuffer[CRW_BYTE_BUFSIZE]; uLongf _bufferSize; ui8 _compressedByteBuffer[CRW_BYTE_BUFSIZE + 12 + 52430]; uLongf _compressedSize; i32 _maxLocationBufferCacheSize; LocationBuffer* _currLocationBuffer; std::queue _locationCacheQueue; std::map _locationCache; std::map::iterator _iterLocationCache; ui32 _chunkOffset, _chunkBlockSize; //the file location for the start of the chunk data ui32 _chunkLength, _oldChunkLength; //length of chunk data, old length of chunk data ui32 _locOffset; //the file location for the start of the location data ui32 _fileSize; bool _isDirtyLocationBuffer; bool _isDone; bool _isThreadFinished; bool _shouldDisableLoading; }; ================================================ FILE: SoA/WorldStructs.cpp ================================================ #include "stdafx.h" #include "WorldStructs.h" #include "BlockData.h" #include "SoaOptions.h" #include "GameManager.h" MultiplePreciseTimer globalMultiplePreciseTimer; ///< for easy global benchmarking AccumulationTimer globalAccumulationTimer; ///< for easy global benchmarking AccumulationTimer globalRenderAccumulationTimer; ///< for easy global benchmarking class Item *ObjectList[OBJECT_LIST_SIZE]; Marker::Marker(const f64v3 &Pos VORB_UNUSED, nString Name VORB_UNUSED, f32v3 Color VORB_UNUSED) : pos(Pos), dist(0.0), name(Name) { // TODO(Ben): implement and remove unused tags } void Marker::Draw(f32m4 &VP VORB_UNUSED, const f64v3 &playerPos VORB_UNUSED) { // TODO(Ben): implement and remove unused tags } ================================================ FILE: SoA/WorldStructs.h ================================================ #pragma once #include #include #include #include "Constants.h" #include "Vertex.h" extern MultiplePreciseTimer globalMultiplePreciseTimer; ///< For easy global benchmarking extern AccumulationTimer globalAccumulationTimer; extern AccumulationTimer globalRenderAccumulationTimer; ///< for easy global benchmarking extern class Item *ObjectList[OBJECT_LIST_SIZE]; const int maxParticles = 10000; struct PlanetGenData; class FixedSizeBillboardVertex{ public: f32v3 pos; GLubyte uv[2]; }; //TODO(Ben): Make this work again class Marker{ public: f64v3 pos; ColorRGBA8 color; int num; double dist; nString name; vg::Texture distText; vg::Texture nameTex; Marker(const f64v3 &Pos, nString Name, const f32v3 Color); void Draw(f32m4 &VP, const f64v3 &playerPos); }; //flags const int TOOSTEEP = 0x4; struct MineralData { MineralData(GLint btype, GLint startheight, float startchance, GLint centerheight, float centerchance, GLint endheight, float endchance, GLint minsize, GLint maxsize) { blockType = btype; startHeight = startheight; startChance = startchance; endHeight = endheight; endChance = endchance; centerHeight = centerheight; centerChance = centerchance; minSize = minsize; maxSize = maxsize; } GLint blockType, startHeight, endHeight, centerHeight, minSize, maxSize; GLfloat startChance, centerChance, endChance; }; class BillboardVertex { public: f32v3 pos; f32v2 uvMult; GLubyte texUnit; GLubyte texID; GLubyte light[2]; GLubyte color[4]; GLubyte size; GLubyte xMod; GLubyte padding[2]; //needs to be 4 byte aligned }; class PhysicsBlockPosLight { public: f32v3 pos; //12 ColorRGB8 color; //15 GLubyte pad1; //16 ColorRGB8 overlayColor; //19 GLubyte pad2; //20 GLubyte light[2]; //22 GLubyte pad3[2]; //24 }; class TreeVertex { public: f32v2 pos; //8 f32v3 center; //20 GLubyte lr, lg, lb, size; //24 GLubyte tr, tg, tb, ltex; //28 }; //No idea how this works. Something to do with prime numbers, but returns # between -1 and 1 inline double PseudoRand(int x, int z) { int n= (x & 0xFFFF) + ((z & 0x7FFF) << 16); n=(n<<13)^n; int nn=(n*(n*n*60493+z*19990303)+x*1376312589)&0x7fffffff; return 1.0-((double)nn/1073741824.0); } inline double PseudoRand(int n) { n = (n << 13) ^ n; int nn = (n*(n*n * 60493 + n * 19990303) + n * 1376312589) & 0x7fffffff; return 1.0 - ((double)nn / 1073741824.0); } ================================================ FILE: SoA/ZipFile.cpp ================================================ #include "stdafx.h" #include "ZipFile.h" #include "Errors.h" ZipFile::ZipFile(nString fileName) : _zipFile(NULL), _failure(0) { _zipFile = unzOpen(fileName.c_str()); if (_zipFile == NULL) { _failure = 1; return; } if (unzGetGlobalInfo(_zipFile, &_globalInfo) != UNZ_OK) { _failure = 1; pError(("could not read file global info in " + fileName).c_str()); unzClose(_zipFile); return; } } ZipFile::~ZipFile() { if (_zipFile != NULL) { unzClose(_zipFile); } } ui8* ZipFile::readFile(nString fileName, size_t& fileSize) { // const int READ_SIZE = 8196; const int MAX_FILENAME = 256; const char dir_delimiter = '/'; unzGoToFirstFile(_zipFile); // Loop to extract all files uLong i; for (i = 0; i < _globalInfo.number_entry; ++i) { // Get info about current file. unz_file_info file_info; char filename[MAX_FILENAME]; if (unzGetCurrentFileInfo( _zipFile, &file_info, filename, MAX_FILENAME, nullptr, 0, nullptr, 0) != UNZ_OK) { printf("could not read file info\n"); return nullptr; } // Check if this entry is a directory or file. const size_t filename_length = strlen(filename); if (filename[filename_length - 1] != dir_delimiter && fileName.size() >= filename_length && fileName == &(filename[filename_length - fileName.size()])) //check that its not a dir and check that it is this file. { unsigned char *buffer = new unsigned char[file_info.uncompressed_size]; // Entry is a file, so extract it. printf("file:%s\n", filename); // fflush(stdout); if (unzOpenCurrentFile(_zipFile) != UNZ_OK) { delete[] buffer; printf("could not open file\n"); return nullptr; } int error = UNZ_OK; error = unzReadCurrentFile(_zipFile, buffer, file_info.uncompressed_size); if (error < 0) { printf("error %d\n", error); unzCloseCurrentFile(_zipFile); delete[] buffer; return nullptr; } fileSize = file_info.uncompressed_size; return buffer; } unzCloseCurrentFile(_zipFile); // Go the the next entry listed in the zip file. if ((i + 1) < _globalInfo.number_entry) { if (unzGoToNextFile(_zipFile) != UNZ_OK) { printf("could not read next file\n"); return nullptr; } } } return nullptr; } ================================================ FILE: SoA/ZipFile.h ================================================ #pragma once #include #include class ZipFile { public: ZipFile(nString fileName); ~ZipFile(); ui8* readFile(nString fileName, size_t& fileSize); bool isFailure() { return _failure; } private: unzFile _zipFile; unz_global_info _globalInfo; bool _failure; }; ================================================ FILE: SoA/app.config ================================================ GraphicsCore: false GraphicsMajor: 3 GraphicsMinor: 2 IsBorderless: false IsFullscreen: false MaxFPS: 60 ScreenHeight: 600 ScreenWidth: 800 SwapInterval: VSync ================================================ FILE: SoA/atomicops.h ================================================ // �2013 Cameron Desrochers. // Distributed under the simplified BSD license /* Copyright (c) 2013, Cameron Desrochers MIT License. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once // Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation // of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment). // Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees). // Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols. #include // Platform detection #if defined(__INTEL_COMPILER) #define AE_ICC #elif defined(_MSC_VER) #define AE_VCPP #elif defined(__GNUC__) #define AE_GCC #endif #if defined(_M_IA64) || defined(__ia64__) #define AE_ARCH_IA64 #elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__) #define AE_ARCH_X64 #elif defined(_M_IX86) || defined(__i386__) #define AE_ARCH_X86 #elif defined(_M_PPC) || defined(__powerpc__) #define AE_ARCH_PPC #else #define AE_ARCH_UNKNOWN #endif // AE_UNUSED #define AE_UNUSED(x) ((void)x) // AE_FORCEINLINE #if defined(AE_VCPP) || defined(AE_ICC) #define AE_FORCEINLINE __forceinline #elif defined(AE_GCC) //#define AE_FORCEINLINE __attribute__((always_inline)) #define AE_FORCEINLINE inline #else #define AE_FORCEINLINE inline #endif // AE_ALIGN #if defined(AE_VCPP) || defined(AE_ICC) #define AE_ALIGN(x) __declspec(align(x)) #elif defined(AE_GCC) #define AE_ALIGN(x) __attribute__((aligned(x))) #else // Assume GCC compliant syntax... #define AE_ALIGN(x) __attribute__((aligned(x))) #endif // Portable atomic fences implemented below: namespace moodycamel { enum memory_order { memory_order_relaxed, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst, // memory_order_sync: Forces a full sync: // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad memory_order_sync = memory_order_seq_cst }; } // end namespace moodycamel #if defined(AE_VCPP) || defined(AE_ICC) // VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences #include #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) #define AeFullSync _mm_mfence #define AeLiteSync _mm_mfence #elif defined(AE_ARCH_IA64) #define AeFullSync __mf #define AeLiteSync __mf #elif defined(AE_ARCH_PPC) #include #define AeFullSync __sync #define AeLiteSync __lwsync #endif #ifdef AE_VCPP #pragma warning(push) #pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert` #endif namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); break; default: assert(false); } } // x86/x64 have a strong memory model -- all loads and stores have // acquire and release semantics automatically (so only need compiler // barriers for those). #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) AE_FORCEINLINE void fence(memory_order order) { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); AeFullSync(); _ReadWriteBarrier(); break; default: assert(false); } } #else AE_FORCEINLINE void fence(memory_order order) { // Non-specialized arch, use heavier memory barriers everywhere just in case :-( switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); AeLiteSync(); _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); AeLiteSync(); _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); AeLiteSync(); _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); AeFullSync(); _ReadWriteBarrier(); break; default: assert(false); } } #endif } // end namespace moodycamel #else // Use standard library of atomics #include namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break; case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break; case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break; case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break; default: assert(false); } } AE_FORCEINLINE void fence(memory_order order) { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break; case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break; case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break; case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break; default: assert(false); } } } // end namespace moodycamel #endif #if !defined(AE_VCPP) || _MSC_VER >= 1700 #define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC #endif #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC #include #endif #include // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: // Provides basic support for atomic variables -- no memory ordering guarantees are provided. // The guarantee of atomicity is only made for types that already have atomic load and store guarantees // at the hardware level -- on most platforms this generally means aligned pointers and integers (only). namespace moodycamel { template class weak_atomic { public: weak_atomic() { } #ifdef AE_VCPP #pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning #endif template weak_atomic(U&& x) : value(std::forward(x)) { } weak_atomic(weak_atomic const& other) : value(other.value) { } weak_atomic(weak_atomic&& other) : value(std::move(other.value)) { } #ifdef AE_VCPP #pragma warning(default: 4100) #endif AE_FORCEINLINE operator T() const { return load(); } #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC template AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value = std::forward(x); return *this; } AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value = other.value; return *this; } AE_FORCEINLINE T load() const { return value; } #else template AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value.store(std::forward(x), std::memory_order_relaxed); return *this; } AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } AE_FORCEINLINE T load() const { return value.load(std::memory_order_relaxed); } #endif private: #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC // No std::atomic support, but still need to circumvent compiler optimizations. // `volatile` will make memory access slow, but is guaranteed to be reliable. volatile T value; #else std::atomic value; #endif }; } // end namespace moodycamel #ifdef AE_VCPP #pragma warning(pop) #endif ================================================ FILE: SoA/errorlog.txt ================================================ *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. *ERROR: FMOD error! (23) File not found. ================================================ FILE: SoA/main.cpp ================================================ #include "stdafx.h" #ifdef VORB_OS_WINDOWS #include #endif #include //#define VORB_IMPL_UI_SDL //#define VORB_IMPL_SOUND_FMOD //#define VORB_IMPL_FONT_SDL #include #include #include "App.h" #include "Startup.h" #include "ConsoleMain.h" // Entry int main(int argc, char **argv) { // Initialize Vorb modules vorb::init(vorb::InitParam::ALL); #ifdef VORB_OS_WINDOWS // Tell windows that our priority class should be above normal SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); #endif // Get the startup mode switch (startup(argc, argv)) { case Startup::APP: // Run the game { App().run(); } break; case Startup::CONSOLE: // Run the console consoleMain(); break; case Startup::HELP: // Pause on user input getchar(); break; default: // Do nothing break; } // Dispose Vorb modules vorb::dispose(vorb::InitParam::ALL); #ifdef VORB_OS_WINDOWS // Need to free the console on windows FreeConsole(); #endif return 0; } ================================================ FILE: SoA/qef.cpp ================================================ /* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * 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 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. * * For more information, please refer to */ #include "stdafx.h" #include "qef.h" #include namespace svd { QefData::QefData() { this->clear(); } QefData::QefData(const float ata_00, const float ata_01, const float ata_02, const float ata_11, const float ata_12, const float ata_22, const float atb_x, const float atb_y, const float atb_z, const float btb, const float massPoint_x, const float massPoint_y, const float massPoint_z, const int numPoints) { this->set(ata_00, ata_01, ata_02, ata_11, ata_12, ata_22, atb_x, atb_y, atb_z, btb, massPoint_x, massPoint_y, massPoint_z, numPoints); } QefData::QefData(const QefData &rhs) { this->set(rhs); } QefData& QefData::operator=(const QefData& rhs) { this->set(rhs); return *this; } void QefData::add(const QefData &rhs) { this->ata_00 += rhs.ata_00; this->ata_01 += rhs.ata_01; this->ata_02 += rhs.ata_02; this->ata_11 += rhs.ata_11; this->ata_12 += rhs.ata_12; this->ata_22 += rhs.ata_22; this->atb_x += rhs.atb_x; this->atb_y += rhs.atb_y; this->atb_z += rhs.atb_z; this->btb += rhs.btb; this->massPoint_x += rhs.massPoint_x; this->massPoint_y += rhs.massPoint_y; this->massPoint_z += rhs.massPoint_z; this->numPoints += rhs.numPoints; } void QefData::clear() { this->set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } void QefData::set(const float ata_00, const float ata_01, const float ata_02, const float ata_11, const float ata_12, const float ata_22, const float atb_x, const float atb_y, const float atb_z, const float btb, const float massPoint_x, const float massPoint_y, const float massPoint_z, const int numPoints) { this->ata_00 = ata_00; this->ata_01 = ata_01; this->ata_02 = ata_02; this->ata_11 = ata_11; this->ata_12 = ata_12; this->ata_22 = ata_22; this->atb_x = atb_x; this->atb_y = atb_y; this->atb_z = atb_z; this->btb = btb; this->massPoint_x = massPoint_x; this->massPoint_y = massPoint_y; this->massPoint_z = massPoint_z; this->numPoints = numPoints; } void QefData::set(const QefData &rhs) { this->set(rhs.ata_00, rhs.ata_01, rhs.ata_02, rhs.ata_11, rhs.ata_12, rhs.ata_22, rhs.atb_x, rhs.atb_y, rhs.atb_z, rhs.btb, rhs.massPoint_x, rhs.massPoint_y, rhs.massPoint_z, rhs.numPoints); } #ifndef NO_OSTREAM std::ostream &operator<<(std::ostream &os, const QefData &qef) { SMat3 ata; Vec3 atb, mp; ata.setSymmetric(qef.ata_00, qef.ata_01, qef.ata_02, qef.ata_11, qef.ata_12, qef.ata_22); atb.set(qef.atb_x, qef.atb_y, qef.atb_z); mp.set(qef.massPoint_x, qef.massPoint_y, qef.massPoint_z); if (qef.numPoints > 0) { VecUtils::scale(mp, 1.0f / qef.numPoints); } os << "QefData [ " << std::endl << " ata =" << std::endl << ata << "," << std::endl << " atb = " << atb << "," << std::endl << " btb = " << qef.btb << "," << std::endl << " massPoint = " << mp << "," << std::endl << " numPoints = " << qef.numPoints << "]"; return os; } #endif QefSolver::QefSolver() : data(), ata(), atb(), massPoint(), x(), hasSolution(false) { } static void normalize(float &nx, float &ny, float &nz) { Vec3 tmpv(nx, ny, nz); VecUtils::normalize(tmpv); nx = tmpv.x; ny = tmpv.y; nz = tmpv.z; } void QefSolver::add(const float px, const float py, const float pz, float nx, float ny, float nz) { this->hasSolution = false; normalize(nx, ny, nz); this->data.ata_00 += nx * nx; this->data.ata_01 += nx * ny; this->data.ata_02 += nx * nz; this->data.ata_11 += ny * ny; this->data.ata_12 += ny * nz; this->data.ata_22 += nz * nz; const float dot = nx * px + ny * py + nz * pz; this->data.atb_x += dot * nx; this->data.atb_y += dot * ny; this->data.atb_z += dot * nz; this->data.btb += dot * dot; this->data.massPoint_x += px; this->data.massPoint_y += py; this->data.massPoint_z += pz; ++this->data.numPoints; } void QefSolver::add(const Vec3 &p, const Vec3 &n) { this->add(p.x, p.y, p.z, n.x, n.y, n.z); } void QefSolver::add(const QefData &rhs) { this->hasSolution = false; this->data.add(rhs); } QefData QefSolver::getData() { return data; } float QefSolver::getError() { if (!this->hasSolution) { throw std::runtime_error("illegal state"); } return this->getError(this->x); } float QefSolver::getError(const Vec3 &pos) { if (!this->hasSolution) { this->setAta(); this->setAtb(); } Vec3 atax; MatUtils::vmul_symmetric(atax, this->ata, pos); return VecUtils::dot(pos, atax) - 2 * VecUtils::dot(pos, this->atb) + this->data.btb; } void QefSolver::reset() { this->hasSolution = false; this->data.clear(); } void QefSolver::setAta() { this->ata.setSymmetric(this->data.ata_00, this->data.ata_01, this->data.ata_02, this->data.ata_11, this->data.ata_12, this->data.ata_22); } void QefSolver::setAtb() { this->atb.set(this->data.atb_x, this->data.atb_y, this->data.atb_z); } float QefSolver::solve(Vec3 &outx, const float svd_tol, const int svd_sweeps, const float pinv_tol) { if (this->data.numPoints == 0) { throw std::invalid_argument("..."); } this->massPoint.set(this->data.massPoint_x, this->data.massPoint_y, this->data.massPoint_z); VecUtils::scale(this->massPoint, 1.0f / this->data.numPoints); this->setAta(); this->setAtb(); Vec3 tmpv; MatUtils::vmul_symmetric(tmpv, this->ata, this->massPoint); VecUtils::sub(this->atb, this->atb, tmpv); this->x.clear(); const float result = Svd::solveSymmetric(this->ata, this->atb, this->x, svd_tol, svd_sweeps, pinv_tol); VecUtils::addScaled(this->x, 1, this->massPoint); this->setAtb(); outx.set(x); this->hasSolution = true; return result; } } ================================================ FILE: SoA/qef.h ================================================ /* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * 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 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. * * For more information, please refer to */ #ifndef QEF_H #define QEF_H #ifndef NO_OSTREAM #include #endif #include "svd.h" namespace svd { class QefData { public: float ata_00, ata_01, ata_02, ata_11, ata_12, ata_22; float atb_x, atb_y, atb_z; float btb; float massPoint_x, massPoint_y, massPoint_z; int numPoints; QefData(); QefData(const float ata_00, const float ata_01, const float ata_02, const float ata_11, const float ata_12, const float ata_22, const float atb_x, const float atb_y, const float atb_z, const float btb, const float massPoint_x, const float massPoint_y, const float massPoint_z, const int numPoints); void add(const QefData &rhs); void clear(); void set(const float ata_00, const float ata_01, const float ata_02, const float ata_11, const float ata_12, const float ata_22, const float atb_x, const float atb_y, const float atb_z, const float btb, const float massPoint_x, const float massPoint_y, const float massPoint_z, const int numPoints); void set(const QefData &rhs); QefData(const QefData &rhs); QefData &operator= (const QefData &rhs); }; #ifndef NO_OSTREAM std::ostream &operator<<(std::ostream &os, const QefData &d); #endif class QefSolver { private: QefData data; SMat3 ata; Vec3 atb, massPoint, x; bool hasSolution; public: QefSolver(); public: const Vec3& getMassPoint() const { return massPoint; } void add(const float px, const float py, const float pz, float nx, float ny, float nz); void add(const Vec3 &p, const Vec3 &n); void add(const QefData &rhs); QefData getData(); float getError(); float getError(const Vec3 &pos); void reset(); float solve(Vec3 &outx, const float svd_tol, const int svd_sweeps, const float pinv_tol); private: QefSolver(const QefSolver &rhs); QefSolver &operator=(const QefSolver &rhs); void setAta(); void setAtb(); }; }; #endif ================================================ FILE: SoA/readerwriterqueue.h ================================================ // �2013 Cameron Desrochers. // Distributed under the simplified BSD license /* Copyright (c) 2013, Cameron Desrochers MIT License. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "atomicops.h" #include #include #include #include #include #include // For malloc/free & size_t // A lock-free queue for a single-consumer, single-producer architecture. // The queue is also wait-free in the common path (except if more memory // needs to be allocated, in which case malloc is called). // Allocates memory sparingly (O(lg(n) times, amortized), and only once if // the original maximum size estimate is never exceeded. // Tested on x86/x64 processors, but semantics should be correct for all // architectures (given the right implementations in atomicops.h), provided // that aligned integer and pointer accesses are naturally atomic. // Note that there should only be one consumer thread and producer thread; // Switching roles of the threads, or using multiple consecutive threads for // one role, is not safe unless properly synchronized. // Using the queue exclusively from one thread is fine, though a bit silly. #define CACHE_LINE_SIZE 64 #ifdef AE_VCPP #pragma warning(push) #pragma warning(disable: 4324) // classure was padded due to __declspec(align()) #pragma warning(disable: 4820) // padding was added #pragma warning(disable: 4127) // conditional expression is constant #endif namespace moodycamel { template class ReaderWriterQueue { // Design: Based on a queue-of-queues. The low-level queues are just // circular buffers with front and tail indices indicating where the // next element to dequeue is and where the next element can be enqueued, // respectively. Each low-level queue is called a "block". Each block // wastes exactly one element's worth of space to keep the design simple // (if front == tail then the queue is empty, and can't be full). // The high-level queue is a circular linked list of blocks; again there // is a front and tail, but this time they are pointers to the blocks. // The front block is where the next element to be dequeued is, provided // the block is not empty. The back block is where elements are to be // enqueued, provided the block is not full. // The producer thread owns all the tail indices/pointers. The consumer // thread owns all the front indices/pointers. Both threads read each // other's variables, but only the owning thread updates them. E.g. After // the consumer reads the producer's tail, the tail may change before the // consumer is done dequeuing an object, but the consumer knows the tail // will never go backwards, only forwards. // If there is no room to enqueue an object, an additional block (of // greater size than the last block) is added. Blocks are never removed. public: // Constructs a queue that can hold maxSize elements without further // allocations. Allocates maxSize + 1, rounded up to the nearest power // of 2, elements. explicit ReaderWriterQueue(size_t maxSize = 15) : largestBlockSize(ceilToPow2(maxSize + 1)) // We need a spare slot to fit maxSize elements in the block #ifndef NDEBUG , enqueuing(false) , dequeuing(false) #endif { assert(maxSize > 0); auto firstBlock = new Block(largestBlockSize); firstBlock->next = firstBlock; frontBlock = firstBlock; tailBlock = firstBlock; // Make sure the reader/writer threads will have the initialized memory setup above: fence(memory_order_sync); } // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. ~ReaderWriterQueue() { // Make sure we get the latest version of all variables from other CPUs: fence(memory_order_sync); // Destroy any remaining objects in queue and free memory Block* tailBlock_ = tailBlock; Block* block = frontBlock; do { Block* nextBlock = block->next; size_t blockFront = block->front; size_t blockTail = block->tail; for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask()) { auto element = reinterpret_cast(block->data + i * sizeof(T)); element->~T(); (void)element; } delete block; block = nextBlock; } while (block != tailBlock_); } // Enqueues a copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. AE_FORCEINLINE bool try_enqueue(T const& element) { return inner_enqueue(element); } // Enqueues a moved copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. AE_FORCEINLINE bool try_enqueue(T&& element) { return inner_enqueue(std::forward(element)); } // Enqueues a copy of element on the queue. // Allocates an additional block of memory if needed. AE_FORCEINLINE void enqueue(T const& element) { inner_enqueue(element); } // Enqueues a moved copy of element on the queue. // Allocates an additional block of memory if needed. AE_FORCEINLINE void enqueue(T&& element) { inner_enqueue(std::forward(element)); } // Attempts to dequeue an element; if the queue is empty, // returns false instead. If the queue has at least one element, // moves front to result using operator=, then returns true. bool try_dequeue(T& result) { #ifndef NDEBUG ReentrantGuard guard(this->dequeuing); #endif // High-level pseudocode: // Remember where the tail block is // If the front block has an element in it, dequeue it // Else // If front block was the tail block when we entered the function, return false // Else advance to next block and dequeue the item there // Note that we have to use the value of the tail block from before we check if the front // block is full or not, in case the front block is empty and then, before we check if the // tail block is at the front block or not, the producer fills up the front block *and // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently // reproducible in practice. Block* tailBlockAtStart = tailBlock; fence(memory_order_acquire); Block* frontBlock_ = frontBlock.load(); size_t blockTail = frontBlock_->tail.load(); size_t blockFront = frontBlock_->front.load(); fence(memory_order_acquire); if (blockFront != blockTail) { // Front block not empty, dequeue from here auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); result = std::move(*element); element->~T(); blockFront = (blockFront + 1) & frontBlock_->sizeMask(); fence(memory_order_release); frontBlock_->front = blockFront; } else if (frontBlock_ != tailBlockAtStart) { // Front block is empty but there's another block ahead, advance to it Block* nextBlock = frontBlock_->next; // Don't need an acquire fence here since next can only ever be set on the tailBlock, // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which // ensures next is up-to-date on this CPU in case we recently were at tailBlock. size_t nextBlockFront = nextBlock->front.load(); size_t nextBlockTail = nextBlock->tail; fence(memory_order_acquire); // Since the tailBlock is only ever advanced after being written to, // we know there's for sure an element to dequeue on it assert(nextBlockFront != nextBlockTail); AE_UNUSED(nextBlockTail); // We're done with this block, let the producer use it if it needs fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue frontBlock = frontBlock_ = nextBlock; compiler_fence(memory_order_release); // Not strictly needed auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); result = std::move(*element); element->~T(); nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask(); fence(memory_order_release); frontBlock_->front = nextBlockFront; } else { // No elements in current block and no other block to advance to return false; } return true; } // Returns a pointer to the first element in the queue (the one that // would be removed next by a call to `try_dequeue`). If the queue appears // empty at the time the method is called, nullptr is returned instead. // Must be called only from the consumer thread. T* peek() { #ifndef NDEBUG ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning Block* tailBlockAtStart = tailBlock; fence(memory_order_acquire); Block* frontBlock_ = frontBlock.load(); size_t blockTail = frontBlock_->tail.load(); size_t blockFront = frontBlock_->front.load(); fence(memory_order_acquire); if (blockFront != blockTail) { return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); } else if (frontBlock_ != tailBlockAtStart) { Block* nextBlock = frontBlock_->next; size_t nextBlockFront = nextBlock->front.load(); fence(memory_order_acquire); assert(nextBlockFront != nextBlock->tail); return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); } return nullptr; } private: enum AllocationMode { CanAlloc, CannotAlloc }; template bool inner_enqueue(U&& element) { #ifndef NDEBUG ReentrantGuard guard(this->enqueuing); #endif // High-level pseudocode (assuming we're allowed to alloc a new block): // If room in tail block, add to tail // Else check next block // If next block is not the head block, enqueue on next block // Else create a new block and enqueue there // Advance tail to the block we just enqueued to Block* tailBlock_ = tailBlock.load(); size_t blockFront = tailBlock_->front.load(); size_t blockTail = tailBlock_->tail.load(); fence(memory_order_acquire); size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask(); if (nextBlockTail != blockFront) { // This block has room for at least one more element char* location = tailBlock_->data + blockTail * sizeof(T); #define new new new (location)T(std::forward(element)); fence(memory_order_release); tailBlock_->tail = nextBlockTail; } else if (tailBlock_->next.load() != frontBlock) { // Note that the reason we can't advance to the frontBlock and start adding new entries there // is because if we did, then dequeue would stay in that block, eventually reading the new values, // instead of advancing to the next full block (whose values were enqueued first and so should be // consumed first). fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock // tailBlock is full, but there's a free block ahead, use it Block* tailBlockNext = tailBlock_->next.load(); size_t nextBlockFront = tailBlockNext->front.load(); nextBlockTail = tailBlockNext->tail.load(); fence(memory_order_acquire); // This block must be empty since it's not the head block and we // go through the blocks in a circle assert(nextBlockFront == nextBlockTail); AE_UNUSED(nextBlockFront); char* location = tailBlockNext->data + nextBlockTail * sizeof(T); new (location)T(std::forward(element)); tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask(); fence(memory_order_release); tailBlock = tailBlockNext; } else if (canAlloc == CanAlloc) { // tailBlock is full and there's no free block ahead; create a new block largestBlockSize *= 2; Block* newBlock = new Block(largestBlockSize); new (newBlock->data) T(std::forward(element)); assert(newBlock->front == 0); newBlock->tail = 1; newBlock->next = tailBlock_->next.load(); tailBlock_->next = newBlock; // Might be possible for the dequeue thread to see the new tailBlock->next // *without* seeing the new tailBlock value, but this is OK since it can't // advance to the next block until tailBlock is set anyway (because the only // case where it could try to read the next is if it's already at the tailBlock, // and it won't advance past tailBlock in any circumstance). fence(memory_order_release); tailBlock = newBlock; } else if (canAlloc == CannotAlloc) { // Would have had to allocate a new block to enqueue, but not allowed return false; } else { assert(false && "Should be unreachable code"); return false; } return true; } // Disable copying ReaderWriterQueue(ReaderWriterQueue const&) {} // Disable assignment ReaderWriterQueue& operator=(ReaderWriterQueue const&) {} AE_FORCEINLINE static size_t ceilToPow2(size_t x) { // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; for (size_t i = 1; i < sizeof(size_t); i <<= 1) { x |= x >> (i << 3); } ++x; return x; } private: #ifndef NDEBUG struct ReentrantGuard { ReentrantGuard(bool& _inSection) : inSection(_inSection) { assert(!inSection); if (inSection) { throw std::runtime_error("ReaderWriterQueue does not support enqueuing or dequeuing elements from other elements' ctors and dtors"); } inSection = true; } ~ReentrantGuard() { inSection = false; } private: ReentrantGuard& operator=(ReentrantGuard const&); private: bool& inSection; }; #endif struct Block { // Avoid false-sharing by putting highly contended variables on their own cache lines AE_ALIGN(CACHE_LINE_SIZE) weak_atomic front; // (Atomic) Elements are read from here AE_ALIGN(CACHE_LINE_SIZE) weak_atomic tail; // (Atomic) Elements are enqueued here AE_ALIGN(CACHE_LINE_SIZE) // next isn't very contended, but we don't want it on the same cache line as tail (which is) weak_atomic next; // (Atomic) char* data; // Contents (on heap) are aligned to T's alignment const size_t size; AE_FORCEINLINE size_t sizeMask() const { return size - 1; } // size must be a power of two (and greater than 0) Block(size_t const& _size) : front(0), tail(0), next(nullptr), size(_size) { // Allocate enough memory for an array of Ts, aligned size_t alignment = std::alignment_of::value; data = rawData = static_cast(std::malloc(sizeof(T)* size + alignment - 1)); assert(rawData); auto alignmentOffset = (uintptr_t)rawData % alignment; if (alignmentOffset != 0) { data += alignment - alignmentOffset; } } ~Block() { std::free(rawData); } private: // C4512 - Assignment operator could not be generated Block& operator=(Block const&); private: char* rawData; }; private: AE_ALIGN(CACHE_LINE_SIZE) weak_atomic frontBlock; // (Atomic) Elements are enqueued to this block AE_ALIGN(CACHE_LINE_SIZE) weak_atomic tailBlock; // (Atomic) Elements are dequeued from this block AE_ALIGN(CACHE_LINE_SIZE) // Ensure tailBlock gets its own cache line size_t largestBlockSize; #ifndef NDEBUG bool enqueuing; bool dequeuing; #endif }; } #ifdef AE_VCPP #pragma warning(pop) #endif ================================================ FILE: SoA/soaUtils.h ================================================ /// /// soaUtils.h /// Seed of Andromeda /// /// Created by Benjamin Arnold on 17 Feb 2015 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// Soa specific generic utilities /// #pragma once #ifndef soaUtils_h__ #define soaUtils_h__ #include "Constants.h" #include #include #include #include #include #include #include #include #include /************************************************************************/ /* Debugging Utilities */ /************************************************************************/ #define PRINT_VEC_TYPE(TYPE, SYM) \ inline void printVec(const char* desc, const TYPE##v2& vec) { \ printf("%s <%"#SYM", %"#SYM">\n", desc, vec.x, vec.y); \ } \ inline void printVec(const char* desc, const TYPE##v3& vec) { \ printf("%s <%"#SYM", %"#SYM", %"#SYM">\n", desc, vec.x, vec.y, vec.z); \ } \ inline void printVec(const char* desc, const TYPE##v4& vec) { \ printf("%s <%"#SYM", %"#SYM", %"#SYM", %"#SYM">\n", desc, vec.x, vec.y, vec.z, vec.w); \ } PRINT_VEC_TYPE(f32, f) PRINT_VEC_TYPE(f64, lf) PRINT_VEC_TYPE(i16, hd) PRINT_VEC_TYPE(i32, d) PRINT_VEC_TYPE(i64, ld) PRINT_VEC_TYPE(ui16, hu) PRINT_VEC_TYPE(ui32, u) PRINT_VEC_TYPE(ui64, lu) #undef PRINT_VEC_TYPE /************************************************************************/ /* Miscellaneous Utilities */ /************************************************************************/ /// Gets the closest point on the AABB to the position /// @param pos: Position to query nearest point in relation to /// @param aabbPos: Position of the -x,-y,-z corner of the aabb /// @param aabbDims: Dimensions of the aabb /// @return the position of the closest point on the aabb inline f32v3 getClosestPointOnAABB(const f32v3& pos, const f32v3& aabbPos, const f32v3& aabbDims) { return f32v3((pos.x <= aabbPos.x) ? aabbPos.x : ((pos.x > aabbPos.x + aabbDims.x) ? (aabbPos.x + aabbDims.x) : pos.x), (pos.y <= aabbPos.y) ? aabbPos.y : ((pos.y > aabbPos.y + aabbDims.y) ? (aabbPos.y + aabbDims.y) : pos.y), (pos.z <= aabbPos.z) ? aabbPos.z : ((pos.z > aabbPos.z + aabbDims.z) ? (aabbPos.z + aabbDims.z) : pos.z)); } inline f64v3 getClosestPointOnAABB(const f64v3& pos, const f64v3& aabbPos, const f64v3& aabbDims) { return f64v3((pos.x <= aabbPos.x) ? aabbPos.x : ((pos.x > aabbPos.x + aabbDims.x) ? (aabbPos.x + aabbDims.x) : pos.x), (pos.y <= aabbPos.y) ? aabbPos.y : ((pos.y > aabbPos.y + aabbDims.y) ? (aabbPos.y + aabbDims.y) : pos.y), (pos.z <= aabbPos.z) ? aabbPos.z : ((pos.z > aabbPos.z + aabbDims.z) ? (aabbPos.z + aabbDims.z) : pos.z)); } /// Moves val towards target in increments of step template inline void stepTowards(T& val, const T& target, const T& step) { if (val < target) { val += step; if (val > target) val = target; } else if (val > target) { val -= step; if (val < target) val = target; } } /// Gets dot product with self, cheaper than vmath::dot because less copy inline f32 selfDot(const f32v3& v) { return v.x * v.x + v.y * v.y + v.z * v.z; } inline f32 selfDot(const f32v4& v) { return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; } inline f64 selfDot(const f64v3& v) { return v.x * v.x + v.y * v.y + v.z * v.z; } inline f64 selfDot(const f64v4& v) { return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; } inline i32 selfDot(const i32v3& v) { return v.x * v.x + v.y * v.y + v.z * v.z; } inline i32 selfDot(const i32v4& v) { return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; } inline ui32 selfDot(const ui32v3& v) { return v.x * v.x + v.y * v.y + v.z * v.z; } inline ui32 selfDot(const ui32v4& v) { return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w; } inline bool dumpFramebufferImage(const nString& rootDir, const ui32v4& viewport) { std::vector pixels; int width = (viewport.z - viewport.x); int height = (viewport.w - viewport.y); pixels.resize(width * height); // Read pixels from framebuffer glReadPixels(viewport.x, viewport.y, viewport.z, viewport.w, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); // Use time to get file path auto now = std::chrono::system_clock::now(); auto in_time_t = std::chrono::system_clock::to_time_t(now); std::stringstream ss; ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d-%X"); nString path = rootDir + "img-" + ss.str() + ".png"; std::replace(path.begin(), path.end(), ':', '-'); // Save screenshot if (!vg::ImageIO().save(path, pixels.data(), width, height, vg::ImageIOFormat::RGBA_UI8)) return false; printf("Made screenshot %s\n", path.c_str()); return true; } // No idea how this works. Something to do with prime numbers, but returns # between -1 and 1 // It has poor distribution inline f64 pseudoRand(int x, int z) { int n = (x & 0xFFFF) + ((z & 0x7FFF) << 16); n = (n << 13) ^ n; int nn = (n*(n*n * 60493 + z * 19990303) + x * 1376312589) & 0x7fffffff; return ((f64)nn / 1073741824.0); } // Thread safe version of intel's fast random number generator inline ui32 fastRand(ui32 seed) { return ((214013u * seed + 2531011u) >> 16) & RAND_MAX; } #define FULL_64 0xFFFFFFFFFFFFFFFFu // Great distribution and only a bit slower than rand() class FastRandGenerator { public: FastRandGenerator() { m_seed[0] = 214013u; m_seed[1] = 2531011u; }; template FastRandGenerator(T seedX) { seed(seedX); } template FastRandGenerator(T seedX, T seedY) { seed(seedX, seedY); } template FastRandGenerator(T seedX, T seedY, T seedZ) { seed(seedX, seedY, seedZ); } // Seeds the generator // TODO(Ben): Not sure how good this seeding is... template inline void seed(T seed) { std::hash h; m_seed[0] = ((ui64)h(seed) << 32) | (ui64)seed; m_seed[1] = m_seed[0] | (m_seed[0] << 32); gen(); } template inline void seed(T seedX, T seedY) { std::hash h; ui64 hx = h(seedX); ui64 hy = h(seedY); m_seed[0] = (ui64)fastRand(hx) | (((ui64)hy + 214013u) << 32); m_seed[1] = (ui64)fastRand(hy) | (m_seed[0] << 32); gen(); } template inline void seed(T seedX, T seedY, T seedZ) { std::hash h; ui64 hx = h(seedX); ui64 hy = h(seedY); m_seed[0] = (ui64)hx | ((ui64)hy << 32); m_seed[1] = ((ui64)hy | ((ui64)hx << 32)) ^ (ui64)seedZ; gen(); } // Generates a random 64 bit number inline ui64 gen() { ui64 x = m_seed[0]; const ui64 y = m_seed[1]; m_seed[0] = y; x ^= x << 23; // a x ^= x >> 17; // b x ^= y ^ (y >> 26); // c m_seed[1] = x; return x + y; } // Generates number between 0 and 1 inline f64 genlf() { ui64 x = m_seed[0]; const ui64 y = m_seed[1]; m_seed[0] = y; x ^= x << 23; // a x ^= x >> 17; // b x ^= y ^ (y >> 26); // c m_seed[1] = x; return (f64)(x + y) / (f64)FULL_64; } private: ui64 m_seed[2]; }; // Math stuff //TODO(Ben): Move this to vorb? // atan2 approximation for doubles for GLSL // using http://lolengine.net/wiki/doc/maths/remez inline f64 fastAtan2(f64 y, f64 x) { const f64 atan_tbl[] = { -3.333333333333333333333333333303396520128e-1, 1.999999117496509842004185053319506031014e-1, -1.428514132711481940637283859690014415584e-1, 1.110012236849539584126568416131750076191e-1, -8.993611617787817334566922323958104463948e-2, 7.212338962134411520637759523226823838487e-2, -5.205055255952184339031830383744136009889e-2, 2.938542391751121307313459297120064977888e-2, -1.079891788348568421355096111489189625479e-2, 1.858552116405489677124095112269935093498e-3 }; /* argument reduction: arctan (-x) = -arctan(x); arctan (1/x) = 1/2 * pi - arctan (x), when x > 0 */ f64 ax = abs(x); f64 ay = abs(y); f64 t0 = ax > ay ? ax : ay; // max f64 t1 = ax < ay ? ax : ay; // min f64 a = 1 / t0; a *= t1; f64 s = a * a; f64 p = atan_tbl[9]; p = fma(fma(fma(fma(fma(fma(fma(fma(fma(fma(p, s, atan_tbl[8]), s, atan_tbl[7]), s, atan_tbl[6]), s, atan_tbl[5]), s, atan_tbl[4]), s, atan_tbl[3]), s, atan_tbl[2]), s, atan_tbl[1]), s, atan_tbl[0]), s*a, a); f64 r = ay > ax ? (1.57079632679489661923 - p) : p; r = x < 0 ? 3.14159265358979323846 - r : r; r = y < 0 ? -r : r; return r; } /// For logarithmic z-buffer shaders inline f32 computeZCoef(f32 zFar) { return 2.0f / log2(zFar + 1.0f); } /// Get distance from a chunk inline f32 computeDistance2FromChunk(const f64v3& chunkPos, const f64v3& p) { f64 dx = ((p.x <= chunkPos.x) ? chunkPos.x : ((p.x > chunkPos.x + CHUNK_WIDTH) ? (chunkPos.x + CHUNK_WIDTH) : p.x)); f64 dy = ((p.y <= chunkPos.y) ? chunkPos.y : ((p.y > chunkPos.y + CHUNK_WIDTH) ? (chunkPos.y + CHUNK_WIDTH) : p.y)); f64 dz = ((p.z <= chunkPos.z) ? chunkPos.z : ((p.z > chunkPos.z + CHUNK_WIDTH) ? (chunkPos.z + CHUNK_WIDTH) : p.z)); dx = dx - p.x; dy = dy - p.y; dz = dz - p.z; // We don't sqrt the distance since sqrt is slow return (f32)(dx*dx + dy*dy + dz*dz); } #endif // soaUtils_h__ ================================================ FILE: SoA/stdafx.cpp ================================================ #include "stdafx.h" ================================================ FILE: SoA/stdafx.h ================================================ /// /// stdafx.h /// Seed of Andromeda /// /// Created by Cristian Zaloj on 29 Dec 2014 /// Copyright 2014 Regrowth Studios /// MIT License /// /// Summary: /// PCH for Seed of Andromeda /// #pragma once #ifndef stdafx_h__SoA #define stdafx_h__SoA //#include /************************************************************************/ /* C Libraries */ /************************************************************************/ #include #include #include /************************************************************************/ /* Stream libraries */ /************************************************************************/ #include #include #include /************************************************************************/ /* STL Containers */ /************************************************************************/ #include #include #include #include #include #include /************************************************************************/ /* Other */ /************************************************************************/ #include #include #include // TODO: Distribute OpenGL from this location #include #endif // stdafx_h__SoA ================================================ FILE: SoA/svd.cpp ================================================ /* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * 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 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. * * For more information, please refer to */ #include "stdafx.h" #include "svd.h" #include namespace svd { Mat3::Mat3() { this->clear(); } Mat3::Mat3(const float m00, const float m01, const float m02, const float m10, const float m11, const float m12, const float m20, const float m21, const float m22) { this->set(m00, m01, m02, m10, m11, m12, m20, m21, m22); } Mat3::Mat3(const Mat3 &rhs) { this->set(rhs); } void Mat3::clear() { this->set(0, 0, 0, 0, 0, 0, 0, 0, 0); } void Mat3::set(const float m00, const float m01, const float m02, const float m10, const float m11, const float m12, const float m20, const float m21, const float m22) { this->m00 = m00; this->m01 = m01; this->m02 = m02; this->m10 = m10; this->m11 = m11; this->m12 = m12; this->m20 = m20; this->m21 = m21; this->m22 = m22; } void Mat3::set(const Mat3 &rhs) { this->set(rhs.m00, rhs.m01, rhs.m02, rhs.m10, rhs.m11, rhs.m12, rhs.m20, rhs.m21, rhs.m22); } void Mat3::setSymmetric(const SMat3 &rhs) { this->setSymmetric(rhs.m00, rhs.m01, rhs.m02, rhs.m11, rhs.m12, rhs.m22); } void Mat3::setSymmetric(const float a00, const float a01, const float a02, const float a11, const float a12, const float a22) { this->set(a00, a01, a02, a01, a11, a12, a02, a12, a22); } SMat3::SMat3() { this->clear(); } SMat3::SMat3(const float m00, const float m01, const float m02, const float m11, const float m12, const float m22) { this->setSymmetric(m00, m01, m02, m11, m12, m22); } SMat3::SMat3(const SMat3 &rhs) { this->setSymmetric(rhs); } void SMat3::clear() { this->setSymmetric(0, 0, 0, 0, 0, 0); } void SMat3::setSymmetric(const SMat3 &rhs) { this->setSymmetric(rhs.m00, rhs.m01, rhs.m02, rhs.m11, rhs.m12, rhs.m22); } void SMat3::setSymmetric(const float a00, const float a01, const float a02, const float a11, const float a12, const float a22) { this->m00 = a00; this->m01 = a01; this->m02 = a02; this->m11 = a11; this->m12 = a12; this->m22 = a22; } Vec3::Vec3() : x(0), y(0), z(0) {} Vec3::Vec3(const Vec3 &rhs)// : Vec3() { this->set(rhs); } Vec3::Vec3(const float x, const float y, const float z)// : Vec3() { this->set(x, y, z); } void Vec3::clear() { this->set(0, 0, 0); } void Vec3::set(const float x, const float y, const float z) { this->x = x; this->y = y; this->z = z; } void Vec3::set(const Vec3 &rhs) { this->set(rhs.x, rhs.y, rhs.z); } #ifndef NO_OSTREAM std::ostream &operator<<(std::ostream &os, const Mat3 &m) { os << "[[" << m.m00 << ", " << m.m01 << ", " << m.m02 << "]" << std::endl; os << " [" << m.m10 << ", " << m.m11 << ", " << m.m12 << "]" << std::endl; os << " [" << m.m20 << ", " << m.m21 << ", " << m.m22 << "]]"; return os; } std::ostream &operator<<(std::ostream &os, const SMat3 &m) { os << "[[" << m.m00 << ", " << m.m01 << ", " << m.m02 << "]" << std::endl; os << " [" << m.m01 << ", " << m.m11 << ", " << m.m12 << "]" << std::endl; os << " [" << m.m02 << ", " << m.m12 << ", " << m.m22 << "]]"; return os; } std::ostream &operator<<(std::ostream &os, const Vec3 &v) { os << "[" << v.x << ", " << v.y << ", " << v.z << "]"; return os; } #endif float MatUtils::fnorm(const Mat3 &a) { return sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) + (a.m10 * a.m10) + (a.m11 * a.m11) + (a.m12 * a.m12) + (a.m20 * a.m20) + (a.m21 * a.m21) + (a.m22 * a.m22)); } float MatUtils::fnorm(const SMat3 &a) { return sqrt((a.m00 * a.m00) + (a.m01 * a.m01) + (a.m02 * a.m02) + (a.m01 * a.m01) + (a.m11 * a.m11) + (a.m12 * a.m12) + (a.m02 * a.m02) + (a.m12 * a.m12) + (a.m22 * a.m22)); } float MatUtils::off(const Mat3 &a) { return sqrt((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m10 * a.m10) + (a.m12 * a.m12) + (a.m20 * a.m20) + (a.m21 * a.m21)); } float MatUtils::off(const SMat3 &a) { return sqrt(2 * ((a.m01 * a.m01) + (a.m02 * a.m02) + (a.m12 * a.m12))); } void MatUtils::mmul(Mat3 &out, const Mat3 &a, const Mat3 &b) { out.set(a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20, a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21, a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22, a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20, a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21, a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22, a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20, a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21, a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22); } void MatUtils::mmul_ata(SMat3 &out, const Mat3 &a) { out.setSymmetric(a.m00 * a.m00 + a.m10 * a.m10 + a.m20 * a.m20, a.m00 * a.m01 + a.m10 * a.m11 + a.m20 * a.m21, a.m00 * a.m02 + a.m10 * a.m12 + a.m20 * a.m22, a.m01 * a.m01 + a.m11 * a.m11 + a.m21 * a.m21, a.m01 * a.m02 + a.m11 * a.m12 + a.m21 * a.m22, a.m02 * a.m02 + a.m12 * a.m12 + a.m22 * a.m22); } void MatUtils::transpose(Mat3 &out, const Mat3 &a) { out.set(a.m00, a.m10, a.m20, a.m01, a.m11, a.m21, a.m02, a.m12, a.m22); } void MatUtils::vmul(Vec3 &out, const Mat3 &a, const Vec3 &v) { out.x = (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z); out.y = (a.m10 * v.x) + (a.m11 * v.y) + (a.m12 * v.z); out.z = (a.m20 * v.x) + (a.m21 * v.y) + (a.m22 * v.z); } void MatUtils::vmul_symmetric(Vec3 &out, const SMat3 &a, const Vec3 &v) { out.x = (a.m00 * v.x) + (a.m01 * v.y) + (a.m02 * v.z); out.y = (a.m01 * v.x) + (a.m11 * v.y) + (a.m12 * v.z); out.z = (a.m02 * v.x) + (a.m12 * v.y) + (a.m22 * v.z); } void VecUtils::addScaled(Vec3 &v, const float s, const Vec3 &rhs) { v.x += s * rhs.x; v.y += s * rhs.y; v.z += s * rhs.z; } float VecUtils::dot(const Vec3 &a, const Vec3 &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } void VecUtils::normalize(Vec3 &v) { const float len2 = VecUtils::dot(v, v); if (fabs(len2) < 1e-12) { v.clear(); } else { VecUtils::scale(v, 1 / sqrt(len2)); } } void VecUtils::scale(Vec3 &v, const float s) { v.x *= s; v.y *= s; v.z *= s; } void VecUtils::sub(Vec3 &c, const Vec3 &a, const Vec3 &b) { const float v0 = a.x - b.x; const float v1 = a.y - b.y; const float v2 = a.z - b.z; c.x = v0; c.y = v1; c.z = v2; } void Givens::rot01_post(Mat3 &m, const float c, const float s) { const float m00 = m.m00, m01 = m.m01, m10 = m.m10, m11 = m.m11, m20 = m.m20, m21 = m.m21; m.set(c * m00 - s * m01, s * m00 + c * m01, m.m02, c * m10 - s * m11, s * m10 + c * m11, m.m12, c * m20 - s * m21, s * m20 + c * m21, m.m22); } void Givens::rot02_post(Mat3 &m, const float c, const float s) { const float m00 = m.m00, m02 = m.m02, m10 = m.m10, m12 = m.m12, m20 = m.m20, m22 = m.m22; m.set(c * m00 - s * m02, m.m01, s * m00 + c * m02, c * m10 - s * m12, m.m11, s * m10 + c * m12, c * m20 - s * m22, m.m21, s * m20 + c * m22); } void Givens::rot12_post(Mat3 &m, const float c, const float s) { const float m01 = m.m01, m02 = m.m02, m11 = m.m11, m12 = m.m12, m21 = m.m21, m22 = m.m22; m.set(m.m00, c * m01 - s * m02, s * m01 + c * m02, m.m10, c * m11 - s * m12, s * m11 + c * m12, m.m20, c * m21 - s * m22, s * m21 + c * m22); } static void calcSymmetricGivensCoefficients(const float a_pp, const float a_pq, const float a_qq, float &c, float &s) { if (a_pq == 0) { c = 1; s = 0; return; } const float tau = (a_qq - a_pp) / (2 * a_pq); const float stt = sqrt(1.0f + tau * tau); const float tan = 1.0f / ((tau >= 0) ? (tau + stt) : (tau - stt)); c = 1.0f / sqrt(1.f + tan * tan); s = tan * c; } void Schur2::rot01(SMat3 &m, float &c, float &s) { svd::calcSymmetricGivensCoefficients(m.m00, m.m01, m.m11, c, s); const float cc = c * c; const float ss = s * s; const float mix = 2 * c * s * m.m01; m.setSymmetric(cc * m.m00 - mix + ss * m.m11, 0, c * m.m02 - s * m.m12, ss * m.m00 + mix + cc * m.m11, s * m.m02 + c * m.m12, m.m22); } void Schur2::rot02(SMat3 &m, float &c, float &s) { svd::calcSymmetricGivensCoefficients(m.m00, m.m02, m.m22, c, s); const float cc = c * c; const float ss = s * s; const float mix = 2 * c * s * m.m02; m.setSymmetric(cc * m.m00 - mix + ss * m.m22, c * m.m01 - s * m.m12, 0, m.m11, s * m.m01 + c * m.m12, ss * m.m00 + mix + cc * m.m22); } void Schur2::rot12(SMat3 &m, float &c, float &s) { svd::calcSymmetricGivensCoefficients(m.m11, m.m12, m.m22, c, s); const float cc = c * c; const float ss = s * s; const float mix = 2 * c * s * m.m12; m.setSymmetric(m.m00, c * m.m01 - s * m.m02, s * m.m01 + c * m.m02, cc * m.m11 - mix + ss * m.m22, 0, ss * m.m11 + mix + cc * m.m22); } static void rotate01(SMat3 &vtav, Mat3 &v) { if (vtav.m01 == 0) { return; } float c, s; Schur2::rot01(vtav, c, s); Givens::rot01_post(v, c, s); } static void rotate02(SMat3 &vtav, Mat3 &v) { if (vtav.m02 == 0) { return; } float c, s; Schur2::rot02(vtav, c, s); Givens::rot02_post(v, c, s); } static void rotate12(SMat3 &vtav, Mat3 &v) { if (vtav.m12 == 0) { return; } float c, s; Schur2::rot12(vtav, c, s); Givens::rot12_post(v, c, s); } void Svd::getSymmetricSvd(const SMat3 &a, SMat3 &vtav, Mat3 &v, const float tol, const int max_sweeps) { vtav.setSymmetric(a); v.set(1, 0, 0, 0, 1, 0, 0, 0, 1); const float delta = tol * MatUtils::fnorm(vtav); for (int i = 0; i < max_sweeps && MatUtils::off(vtav) > delta; ++i) { rotate01(vtav, v); rotate02(vtav, v); rotate12(vtav, v); } } //static float calcError(const Mat3 &A, const Vec3 &x, // const Vec3 &b) { // Vec3 vtmp; // MatUtils::vmul(vtmp, A, x); // VecUtils::sub(vtmp, b, vtmp); // return VecUtils::dot(vtmp, vtmp); //} static float calcError(const SMat3 &origA, const Vec3 &x, const Vec3 &b) { Mat3 A; Vec3 vtmp; A.setSymmetric(origA); MatUtils::vmul(vtmp, A, x); VecUtils::sub(vtmp, b, vtmp); return VecUtils::dot(vtmp, vtmp); } static float pinv(const float x, const float tol) { return (fabs(x) < tol || fabs(1 / x) < tol) ? 0 : (1 / x); } void Svd::pseudoinverse(Mat3 &out, const SMat3 &d, const Mat3 &v, const float tol) { const float d0 = pinv(d.m00, tol), d1 = pinv(d.m11, tol), d2 = pinv(d.m22, tol); out.set(v.m00 * d0 * v.m00 + v.m01 * d1 * v.m01 + v.m02 * d2 * v.m02, v.m00 * d0 * v.m10 + v.m01 * d1 * v.m11 + v.m02 * d2 * v.m12, v.m00 * d0 * v.m20 + v.m01 * d1 * v.m21 + v.m02 * d2 * v.m22, v.m10 * d0 * v.m00 + v.m11 * d1 * v.m01 + v.m12 * d2 * v.m02, v.m10 * d0 * v.m10 + v.m11 * d1 * v.m11 + v.m12 * d2 * v.m12, v.m10 * d0 * v.m20 + v.m11 * d1 * v.m21 + v.m12 * d2 * v.m22, v.m20 * d0 * v.m00 + v.m21 * d1 * v.m01 + v.m22 * d2 * v.m02, v.m20 * d0 * v.m10 + v.m21 * d1 * v.m11 + v.m22 * d2 * v.m12, v.m20 * d0 * v.m20 + v.m21 * d1 * v.m21 + v.m22 * d2 * v.m22); } float Svd::solveSymmetric(const SMat3 &A, const Vec3 &b, Vec3 &x, const float svd_tol, const int svd_sweeps, const float pinv_tol) { Mat3 mtmp, pinv, V; SMat3 VTAV; Svd::getSymmetricSvd(A, VTAV, V, svd_tol, svd_sweeps); Svd::pseudoinverse(pinv, VTAV, V, pinv_tol); MatUtils::vmul(x, pinv, b); return svd::calcError(A, x, b); } float LeastSquares::solveLeastSquares(const Mat3 &a, const Vec3 &b, Vec3 &x, const float svd_tol, const int svd_sweeps, const float pinv_tol) { Mat3 at; SMat3 ata; Vec3 atb; MatUtils::transpose(at, a); MatUtils::mmul_ata(ata, a); MatUtils::vmul(atb, at, b); return Svd::solveSymmetric(ata, atb, x, svd_tol, svd_sweeps, pinv_tol); } } ================================================ FILE: SoA/svd.h ================================================ /* * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * 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 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. * * For more information, please refer to */ #ifndef SVD_H #define SVD_H #ifndef NO_OSTREAM #include #endif namespace svd { class SMat3 { public: float m00, m01, m02, m11, m12, m22; public: SMat3(); SMat3(const float m00, const float m01, const float m02, const float m11, const float m12, const float m22); void clear(); void setSymmetric(const float m00, const float m01, const float m02, const float m11, const float m12, const float m22); void setSymmetric(const SMat3 &rhs); private: SMat3(const SMat3 &rhs); SMat3 &operator=(const SMat3 &rhs); }; class Mat3 { public: float m00, m01, m02, m10, m11, m12, m20, m21, m22; public: Mat3(); Mat3(const float m00, const float m01, const float m02, const float m10, const float m11, const float m12, const float m20, const float m21, const float m22); void clear(); void set(const float m00, const float m01, const float m02, const float m10, const float m11, const float m12, const float m20, const float m21, const float m22); void set(const Mat3 &rhs); void setSymmetric(const float a00, const float a01, const float a02, const float a11, const float a12, const float a22); void setSymmetric(const SMat3 &rhs); private: Mat3(const Mat3 &rhs); Mat3 &operator=(const Mat3 &rhs); }; class Vec3 { public: float x, y, z; public: Vec3(); Vec3(const float x, const float y, const float z); void clear(); void set(const float x, const float y, const float z); void set(const Vec3 &rhs); private: Vec3(const Vec3 &rhs); Vec3 &operator=(const Vec3 &rhs); }; #ifndef NO_OSTREAM std::ostream &operator<<(std::ostream &os, const Mat3 &m); std::ostream &operator<<(std::ostream &os, const SMat3 &m); std::ostream &operator<<(std::ostream &os, const Vec3 &v); #endif class MatUtils { public: static float fnorm(const Mat3 &a); static float fnorm(const SMat3 &a); static float off(const Mat3 &a); static float off(const SMat3 &a); public: static void mmul(Mat3 &out, const Mat3 &a, const Mat3 &b); static void mmul_ata(SMat3 &out, const Mat3 &a); static void transpose(Mat3 &out, const Mat3 &a); static void vmul(Vec3 &out, const Mat3 &a, const Vec3 &v); static void vmul_symmetric(Vec3 &out, const SMat3 &a, const Vec3 &v); }; class VecUtils { public: static void addScaled(Vec3 &v, const float s, const Vec3 &rhs); static float dot(const Vec3 &a, const Vec3 &b); static void normalize(Vec3 &v); static void scale(Vec3 &v, const float s); static void sub(Vec3 &c, const Vec3 &a, const Vec3 &b); }; class Givens { public: static void rot01_post(Mat3 &m, const float c, const float s); static void rot02_post(Mat3 &m, const float c, const float s); static void rot12_post(Mat3 &m, const float c, const float s); }; class Schur2 { public: static void rot01(SMat3 &out, float &c, float &s); static void rot02(SMat3 &out, float &c, float &s); static void rot12(SMat3 &out, float &c, float &s); }; class Svd { public: static void getSymmetricSvd(const SMat3 &a, SMat3 &vtav, Mat3 &v, const float tol, const int max_sweeps); static void pseudoinverse(Mat3 &out, const SMat3 &d, const Mat3 &v, const float tol); static float solveSymmetric(const SMat3 &A, const Vec3 &b, Vec3 &x, const float svd_tol, const int svd_sweeps, const float pinv_tol); }; class LeastSquares { public: static float solveLeastSquares(const Mat3 &a, const Vec3 &b, Vec3 &x, const float svd_tol, const int svd_sweeps, const float pinv_tol); }; }; #endif ================================================ FILE: SoA/textureUtils.h ================================================ // // textureUtils.h // Seed of Andromeda // // Created by Benjamin Arnold on 2 Mar 2016 // Copyright 2014 Regrowth Studios // MIT License // // Summary: // // #pragma once #ifndef textureUtils_h__ #define textureUtils_h__ #include "Constants.h" #include #include #include #include #include #include #include #include #include #include inline void initSinglePixelTexture(VGTexture& texture, color4 pixel) { glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixel[0]); glBindTexture(GL_TEXTURE_2D, 0); }; #endif // textureUtils_h__ ================================================ FILE: VorbCopy.bat ================================================ @ECHO OFF IF DEFINED VORB_PATH ( GOTO PATH_ENV ) IF EXIST Vorb\ ( GOTO PATH_SAME ) IF EXIST ..\Vorb\ ( GOTO PATH_ROOT ) IF EXIST deps\Vorb\ ( GOTO PATH_DEPS ) ECHO No path for Vorb found, please add to the environment variables as "VORB_PATH" PAUSE EXIT 1 :PATH_ENV ECHO Using VORB_PATH environment variable GOTO VORB_COPY :PATH_SAME ECHO Vorb was found in the same directory SET VORB_PATH=Vorb GOTO VORB_COPY :PATH_ROOT ECHO Vorb was found in the parent directory SET VORB_PATH=..\Vorb GOTO VORB_COPY :PATH_DEPS ECHO Vorb was found in the deps directory SET VORB_PATH=deps\Vorb GOTO VORB_COPY :VORB_COPY PAUSE ROBOCOPY /E "%VORB_PATH%\include" "deps\include\Vorb" * ROBOCOPY /E "%VORB_PATH%\deps\lib\Win32" "deps\lib\Win32" * ROBOCOPY /E "%VORB_PATH%\deps\lib\x64" "deps\lib\x64" * COPY /Y "%VORB_PATH%\bin\Win32\Release\Vorb.lib " "deps\lib\Win32\Vorb.lib" COPY /Y "%VORB_PATH%\bin\Win32\Debug\Vorb-d.lib " "deps\lib\Win32\Vorb-d.lib" COPY /Y "%VORB_PATH%\bin\x64\Release\Vorb.lib " "deps\lib\x64\Vorb.lib" COPY /Y "%VORB_PATH%\bin\x64\Debug\Vorb-d.lib " "deps\lib\x64\Vorb-d.lib" EXIT 0 ================================================ FILE: appveyor.yml ================================================ # Windows (https://github.com/travis-ci-tester/toolchain-table) environment: global: # This variable used only if '--upload' added to 'jenkins.py' GITHUB_USER_PASSWORD: secure: wG0IMoOCj/h0cQ2sClQlkHIy3JNaqt6VYJ0E3AY8pwnCeFeFFcufmrRxdKw7PQ7E #soa # secure: 8msFf0QF4JUIjZKukMnJItvIQWa+1gU2Hv/k0StI0HtzeFP1fJddEBcqIIQd4+nw #krazer matrix: # compile issues with VS2017 # - TOOLCHAIN: "ninja-vs-15-2017-win64-cxx17" # PROJECT_DIR: .\ # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 # # - TOOLCHAIN: "nmake-vs-15-2017-win64-cxx17" # PROJECT_DIR: .\ # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 # - TOOLCHAIN: "vs-15-2017-win64-cxx17" PROJECT_DIR: .\ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 HUNTER_BINARY_DIR: C:\__BIN - TOOLCHAIN: "vs-14-2015-win64" PROJECT_DIR: .\ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 HUNTER_BINARY_DIR: C:\__BIN # - TOOLCHAIN: "mingw-cxx17" # PROJECT_DIR: .\ # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 # # - TOOLCHAIN: "msys-cxx17" # PROJECT_DIR: .\ # APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 install: # Python 3 - cmd: set PATH=C:\Python34-x64;C:\Python34-x64\Scripts;%PATH% # Install Python package 'requests', 'gitpython' - cmd: pip install requests - cmd: pip install gitpython # Install latest Polly toolchains and scripts - cmd: appveyor DownloadFile https://github.com/ruslo/polly/archive/master.zip - cmd: 7z x master.zip - cmd: set POLLY_ROOT=%cd%\polly-master # Install dependencies (CMake, Ninja) - cmd: python %POLLY_ROOT%\bin\install-ci-dependencies.py # Tune locations - cmd: set PATH=%cd%\_ci\cmake\bin;%PATH% - cmd: set PATH=%cd%\_ci\ninja;%PATH% - cmd: git submodule update --init Vorb # Remove entry with sh.exe from PATH to fix error with MinGW toolchain # (For MinGW make to work correctly sh.exe must NOT be in your path) # * http://stackoverflow.com/a/3870338/2288008 - cmd: set PATH=%PATH:C:\Program Files\Git\usr\bin;=% # Save git.exe in HUNTER_GIT_EXECUTABLE for upload # * https://docs.hunter.sh/en/latest/reference/user-variables.html#hunter-git-executable # Variable will be used in CMake so it's okay to use Unix style '/' - cmd: set HUNTER_GIT_EXECUTABLE=C:/Program Files/Git/bin/git.exe - cmd: set MINGW_PATH=C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin # MSYS2 location - cmd: set MSYS_PATH=C:\msys64\usr\bin # Visual Studio 15 2017: Mimic behavior of older versions - cmd: set VS150COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools build_script: - cmd: python .\jenkins.py # http://www.appveyor.com/docs/branches#white-and-blacklisting # Exclude branch 'pkg.template'. Nothing to build there. branches: except: - pkg.template - /^pr\..*/ - /^v[0-9]+\.[0-9]+\.[0-9]+$/ ================================================ FILE: build.bat ================================================ @ECHO OFF && PUSHD "%~dp0" && SETLOCAL SET VS_TARGET="2015" SET BUILD_CLEAN="false" SET "CMAKE_PARAMS=" SET "BUILD_PARAMS=" GOTO Argloop :Help ECHO "\n" ECHO " /--------------\\ \n | Build Help |\n \\--------------/\n\n" ECHO " Build flags:\n" ECHO " --clean | -c --- Clean build, removes all previous artefacts.\n" ECHO " CMake flags:\n" ECHO " --release | -r --- Compile in release mode.\n" ECHO " --debug | -d --- Compile in debug mode.\n" ECHO " --cxx17 | -17 --- Target C++17 (otherwise targets C++14).\n" ECHO " --no-gdb | -ng --- Add OS specific debug symbols rather than GDB's.\n" ECHO " --no-extra-debug | -ned --- Don't add extra debug symbols.\n" ECHO " --no-optimise-debug | -nod --- Don't optimise debug mode builds.\n" ECHO " Make flags:\n" ECHO " --verbose | -v --- Run make with verbose set on.\n" ECHO "\n" GOTO ArgloopContinue :Clean SET BUILD_CLEAN="true" SET "BUILD_PARAMS=%BUILD_PARAMS% --clean-first" GOTO ArgloopContinue :Release SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DCMAKE_BUILD_TYPE=Release" GOTO ArgloopContinue :Debug SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DCMAKE_BUILD_TYPE=Debug" GOTO ArgloopContinue :Cxx17 SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DTARGET_CXX_17=On" GOTO ArgloopContinue :NoGDB SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DUSING_GDB=Off" GOTO ArgloopContinue :NoExtraDebug SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DEXTRA_DEBUG=Off" GOTO ArgloopContinue :NoOptimiseDebug SET "CMAKE_PARAMS=%CMAKE_PARAMS% -DOPTIMISE_ON_DEBUG=Off" GOTO ArgloopContinue :Verbose SET "BUILD_PARAMS=%BUILD_PARAMS% -- VERBOSE=1" GOTO ArgloopContinue :Argloop IF "%1"=="-h" ( GOTO Help ) ELSE IF "%1"=="--help" ( GOTO Help ) ELSE IF "%1"=="-c" ( GOTO Clean ) ELSE IF "%1"=="--clean" ( GOTO Clean ) ELSE IF "%1"=="-r" ( GOTO Release ) ELSE IF "%1"=="--release" ( GOTO Release ) ELSE IF "%1"=="-d" ( GOTO Debug ) ELSE IF "%1"=="--debug" ( GOTO Debug ) ELSE IF "%1"=="-17" ( GOTO Cxx17 ) ELSE IF "%1"=="--cxx17" ( GOTO Cxx17 ) ELSE IF "%1"=="-ng" ( GOTO NoGDB ) ELSE IF "%1"=="--no-gdb" ( GOTO NoGDB ) ELSE IF "%1"=="-ned" ( GOTO NoExtraDebug ) ELSE IF "%1"=="--no-extra-debug" ( GOTO NoExtraDebug ) ELSE IF "%1"=="-nod" ( GOTO NoOptimiseDebug ) ELSE IF "%1"=="--no-optimise-debug" ( GOTO NoOptimiseDebug ) ELSE IF "%1"=="-v" ( GOTO Verbose ) ELSE IF "%1"=="--verbose" ( GOTO Verbose ) ELSE IF NOT "%1"=="" ( ECHO "Error: Do not recognise argument %1." EXIT /B 1 ) ELSE ( GOTO ArgloopBreak ) :ArgloopContinue SHIFT GOTO Argloop :ArgloopBreak IF EXIST "build" ( IF "%BUILD_CLEAN%"=="true" ( RMDIR build /S /Q ) ) ELSE ( MKDIR build ) CD build SET "CMAKE_COMMAND=cmake %CMAKE_PARAMS% -G "Visual Studio 14 2015 Win64" ../" %CMAKE_COMMAND% SET "BUILD_COMMAND=cmake --build . %MAKE_PARAMS%" %BUILD_COMMAND% CD ../ ================================================ FILE: build.sh ================================================ #!/bin/bash BUILD_CLEAN=false BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug" CMAKE_PARAMS="" MAKE_PARAMS="" while test $# -gt 0 do case "$1" in -h|--help) printf -- "\n" printf -- " /--------------\\ \n | Build Help |\n \\--------------/\n\n" printf -- " Build flags:\n" printf -- " --clean | -c --- Clean build, removes all previous artefacts.\n" printf -- " CMake flags:\n" printf -- " --release | -r --- Compile in release mode.\n" printf -- " --debug | -d --- Compile in debug mode.\n" printf -- " --cxx17 | -17 --- Target C++17 (otherwise targets C++14).\n" printf -- " --clang | -cl --- Compiles using clang rather than gcc.\n" printf -- " --no-gdb | -ng --- Add OS specific debug symbols rather than GDB's.\n" printf -- " --no-extra-debug | -ned --- Don't add extra debug symbols.\n" printf -- " --no-optimise-debug | -nod --- Don't optimise debug mode builds.\n" printf -- " Make flags:\n" printf -- " --jobs X | -j X --- Run compilation on X number of threads.\n" printf -- " --verbose | -v --- Run make with verbose set on.\n" printf -- "\n" exit 0 ;; -c|--clean) BUILD_CLEAN=true ;; -r|--release) BUILD_TYPE="-DCMAKE_BUILD_TYPE=Release" ;; -d|--debug) BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug" ;; -17|--cxx17) CMAKE_PARAMS="$CMAKE_PARAMS -DTARGET_CXX_17=On" ;; -cl|--clang) CMAKE_PARAMS="$CMAKE_PARAMS -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_C_COMPILER=/usr/bin/clang" ;; -ng|--no-gdb) CMAKE_PARAMS="$CMAKE_PARAMS -DUSING_GDB=Off" ;; -ned|--no-extra-debug) CMAKE_PARAMS="$CMAKE_PARAMS -DEXTRA_DEBUG=Off" ;; -nod|--no-optimise-debug) CMAKE_PARAMS="$CMAKE_PARAMS -DOPTIMISE_ON_DEBUG=Off" ;; -j|--jobs) if ! [[ $2 =~ ^[0-9]+$ ]] ; then echo "Error: Saw argument --jobs but it was not followed by a number of jobs to run." exit 1 fi MAKE_PARAMS="$MAKE_PARAMS -j$2" shift ;; -v|--verbose) MAKE_PARAMS="$MAKE_PARAMS VERBOSE=1" ;; *) echo "Error: Do not recognise argument $1." exit 1 ;; esac shift done if [ -d "build" ] ; then if [ "$BUILD_CLEAN" = true ] ; then rm -rf build/* fi else mkdir build fi cd build eval "cmake ../ $CMAKE_PARAMS $BUILD_TYPE" eval "make $MAKE_PARAMS" cd ../ ================================================ FILE: cmake/hunter/HunterGate.cmake ================================================ # Copyright (c) 2013-2019, Ruslan Baratov # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This is a gate file to Hunter package manager. # Include this file using `include` command and add package you need, example: # # cmake_minimum_required(VERSION 3.2) # # include("cmake/HunterGate.cmake") # HunterGate( # URL "https://github.com/path/to/hunter/archive.tar.gz" # SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" # ) # # project(MyProject) # # hunter_add_package(Foo) # hunter_add_package(Boo COMPONENTS Bar Baz) # # Projects: # * https://github.com/hunter-packages/gate/ # * https://github.com/ruslo/hunter option(HUNTER_ENABLED "Enable Hunter package manager support" ON) if(HUNTER_ENABLED) if(CMAKE_VERSION VERSION_LESS "3.2") message( FATAL_ERROR "At least CMake version 3.2 required for Hunter dependency management." " Update CMake or set HUNTER_ENABLED to OFF." ) endif() endif() include(CMakeParseArguments) # cmake_parse_arguments option(HUNTER_STATUS_PRINT "Print working status" ON) option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors") function(hunter_gate_status_print) if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) foreach(print_message ${ARGV}) message(STATUS "[hunter] ${print_message}") endforeach() endif() endfunction() function(hunter_gate_status_debug) if(HUNTER_STATUS_DEBUG) foreach(print_message ${ARGV}) string(TIMESTAMP timestamp) message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") endforeach() endif() endfunction() function(hunter_gate_error_page error_page) message("------------------------------ ERROR ------------------------------") message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") message("-------------------------------------------------------------------") message("") message(FATAL_ERROR "") endfunction() function(hunter_gate_internal_error) message("") foreach(print_message ${ARGV}) message("[hunter ** INTERNAL **] ${print_message}") endforeach() message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_error_page("error.internal") endfunction() function(hunter_gate_fatal_error) cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") if("${hunter_ERROR_PAGE}" STREQUAL "") hunter_gate_internal_error("Expected ERROR_PAGE") endif() message("") foreach(x ${hunter_UNPARSED_ARGUMENTS}) message("[hunter ** FATAL ERROR **] ${x}") endforeach() message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") hunter_gate_error_page("${hunter_ERROR_PAGE}") endfunction() function(hunter_gate_user_error) hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") endfunction() function(hunter_gate_self root version sha1 result) string(COMPARE EQUAL "${root}" "" is_bad) if(is_bad) hunter_gate_internal_error("root is empty") endif() string(COMPARE EQUAL "${version}" "" is_bad) if(is_bad) hunter_gate_internal_error("version is empty") endif() string(COMPARE EQUAL "${sha1}" "" is_bad) if(is_bad) hunter_gate_internal_error("sha1 is empty") endif() string(SUBSTRING "${sha1}" 0 7 archive_id) set( hunter_self "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" ) set("${result}" "${hunter_self}" PARENT_SCOPE) endfunction() # Set HUNTER_GATE_ROOT cmake variable to suitable value. function(hunter_gate_detect_root) # Check CMake variable string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) if(not_empty) set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") return() endif() # Check environment variable string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) if(not_empty) set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") return() endif() # Check HOME environment variable string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") return() endif() # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) if(WIN32) string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using SYSTEMDRIVE environment variable" ) return() endif() string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) if(result) set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) hunter_gate_status_debug( "HUNTER_ROOT set using USERPROFILE environment variable" ) return() endif() endif() hunter_gate_fatal_error( "Can't detect HUNTER_ROOT" ERROR_PAGE "error.detect.hunter.root" ) endfunction() function(hunter_gate_download dir) string( COMPARE NOTEQUAL "$ENV{HUNTER_DISABLE_AUTOINSTALL}" "" disable_autoinstall ) if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) hunter_gate_fatal_error( "Hunter not found in '${dir}'" "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" "Settings:" " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" ERROR_PAGE "error.run.install" ) endif() string(COMPARE EQUAL "${dir}" "" is_bad) if(is_bad) hunter_gate_internal_error("Empty 'dir' argument") endif() string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") endif() string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) if(is_bad) hunter_gate_internal_error("HUNTER_GATE_URL empty") endif() set(done_location "${dir}/DONE") set(sha1_location "${dir}/SHA1") set(build_dir "${dir}/Build") set(cmakelists "${dir}/CMakeLists.txt") hunter_gate_status_debug("Locking directory: ${dir}") file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) hunter_gate_status_debug("Lock done") if(EXISTS "${done_location}") # while waiting for lock other instance can do all the job hunter_gate_status_debug("File '${done_location}' found, skip install") return() endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(MAKE_DIRECTORY "${build_dir}") # check directory permissions # Disabling languages speeds up a little bit, reduces noise in the output # and avoids path too long windows error file( WRITE "${cmakelists}" "cmake_minimum_required(VERSION 3.2)\n" "project(HunterDownload LANGUAGES NONE)\n" "include(ExternalProject)\n" "ExternalProject_Add(\n" " Hunter\n" " URL\n" " \"${HUNTER_GATE_URL}\"\n" " URL_HASH\n" " SHA1=${HUNTER_GATE_SHA1}\n" " DOWNLOAD_DIR\n" " \"${dir}\"\n" " TLS_VERIFY\n" " ${HUNTER_TLS_VERIFY}\n" " SOURCE_DIR\n" " \"${dir}/Unpacked\"\n" " CONFIGURE_COMMAND\n" " \"\"\n" " BUILD_COMMAND\n" " \"\"\n" " INSTALL_COMMAND\n" " \"\"\n" ")\n" ) if(HUNTER_STATUS_DEBUG) set(logging_params "") else() set(logging_params OUTPUT_QUIET) endif() hunter_gate_status_debug("Run generate") # Need to add toolchain file too. # Otherwise on Visual Studio + MDD this will fail with error: # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") else() # 'toolchain_arg' can't be empty set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") endif() string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) if(no_make) set(make_arg "") else() # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") endif() execute_process( COMMAND "${CMAKE_COMMAND}" "-H${dir}" "-B${build_dir}" "-G${CMAKE_GENERATOR}" "${toolchain_arg}" ${make_arg} WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error( "Configure project failed." "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" "In directory ${dir}" ) endif() hunter_gate_status_print( "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" " ${HUNTER_GATE_URL}" " -> ${dir}" ) execute_process( COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) hunter_gate_internal_error("Build project failed") endif() file(REMOVE_RECURSE "${build_dir}") file(REMOVE_RECURSE "${cmakelists}") file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") file(WRITE "${done_location}" "DONE") hunter_gate_status_debug("Finished") endfunction() # Must be a macro so master file 'cmake/Hunter' can # apply all variables easily just by 'include' command # (otherwise PARENT_SCOPE magic needed) macro(HunterGate) if(HUNTER_GATE_DONE) # variable HUNTER_GATE_DONE set explicitly for external project # (see `hunter_download`) set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() # First HunterGate command will init Hunter, others will be ignored get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) if(NOT HUNTER_ENABLED) # Empty function to avoid error "unknown function" function(hunter_add_package) endfunction() set( _hunter_gate_disabled_mode_dir "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" ) if(EXISTS "${_hunter_gate_disabled_mode_dir}") hunter_gate_status_debug( "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" ) list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") endif() elseif(_hunter_gate_done) hunter_gate_status_debug("Secondary HunterGate (use old settings)") hunter_gate_self( "${HUNTER_CACHED_ROOT}" "${HUNTER_VERSION}" "${HUNTER_SHA1}" _hunter_self ) include("${_hunter_self}/cmake/Hunter") else() set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) if(_have_project_name) hunter_gate_fatal_error( "Please set HunterGate *before* 'project' command. " "Detected project: ${PROJECT_NAME}" ERROR_PAGE "error.huntergate.before.project" ) endif() cmake_parse_arguments( HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} ) string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) string( COMPARE NOTEQUAL "${HUNTER_GATE_UNPARSED_ARGUMENTS}" "" _have_unparsed ) string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) if(_have_unparsed) hunter_gate_user_error( "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" ) endif() if(_empty_sha1) hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") endif() if(_empty_url) hunter_gate_user_error("URL suboption of HunterGate is mandatory") endif() if(_have_global) if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") endif() endif() if(HUNTER_GATE_LOCAL) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") endif() if(_have_filepath) hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") endif() endif() if(_have_filepath) if(_have_global) hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") endif() if(HUNTER_GATE_LOCAL) hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") endif() endif() hunter_gate_detect_root() # set HUNTER_GATE_ROOT # Beautify path, fix probable problems with windows path slashes get_filename_component( HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE ) hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") if(NOT HUNTER_ALLOW_SPACES_IN_PATH) string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) if(NOT _contain_spaces EQUAL -1) hunter_gate_fatal_error( "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" "(Use at your own risk!)" ERROR_PAGE "error.spaces.in.hunter.root" ) endif() endif() string( REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" HUNTER_GATE_VERSION "${HUNTER_GATE_URL}" ) string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) if(_is_empty) set(HUNTER_GATE_VERSION "unknown") endif() hunter_gate_self( "${HUNTER_GATE_ROOT}" "${HUNTER_GATE_VERSION}" "${HUNTER_GATE_SHA1}" _hunter_self ) set(_master_location "${_hunter_self}/cmake/Hunter") get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) set(_done_location "${_archive_id_location}/DONE") set(_sha1_location "${_archive_id_location}/SHA1") # Check Hunter already downloaded by HunterGate if(NOT EXISTS "${_done_location}") hunter_gate_download("${_archive_id_location}") endif() if(NOT EXISTS "${_done_location}") hunter_gate_internal_error("hunter_gate_download failed") endif() if(NOT EXISTS "${_sha1_location}") hunter_gate_internal_error("${_sha1_location} not found") endif() file(READ "${_sha1_location}" _sha1_value) string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) if(NOT _is_equal) hunter_gate_internal_error( "Short SHA1 collision:" " ${_sha1_value} (from ${_sha1_location})" " ${HUNTER_GATE_SHA1} (HunterGate)" ) endif() if(NOT EXISTS "${_master_location}") hunter_gate_user_error( "Master file not found:" " ${_master_location}" "try to update Hunter/HunterGate" ) endif() include("${_master_location}") set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) endif() endmacro() ================================================ FILE: cmake/hunter/cache.cmake ================================================ set( HUNTER_CACHE_SERVERS "https://github.com/huntercache/main" CACHE STRING "Default cache server" ) string(COMPARE EQUAL "$ENV{TRAVIS}" "true" is_travis) string(COMPARE EQUAL "$ENV{APPVEYOR}" "True" is_appveyor) string(COMPARE EQUAL "$ENV{GITHUB_USER_PASSWORD}" "" password_is_empty) if((is_travis OR is_appveyor) AND NOT password_is_empty) option(HUNTER_RUN_UPLOAD "Upload cache binaries" ON) endif() message(STATUS "Travis: ${is_travis}") message(STATUS "GITHUB_USER_PASSWORD: $ENV{GITHUB_USER_PASSWORD}") message(STATUS "Password empty: ${password_is_empty}") message(STATUS "Hunter upload: ${HUNTER_RUN_UPLOAD}") set( HUNTER_PASSWORDS_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/passwords.cmake" CACHE FILEPATH "Hunter passwords" ) ================================================ FILE: cmake/hunter/passwords.cmake ================================================ # cmake/Hunter/passwords.cmake hunter_upload_password( REPO_OWNER "huntercache" REPO "SoA" USERNAME "Regrowth-Studios-Bot" PASSWORD "$ENV{GITHUB_USER_PASSWORD}" ) ================================================ FILE: jenkins.py ================================================ #!/usr/bin/env python3 # Copyright (c) 2014, Ruslan Baratov # All rights reserved. # https://github.com/ruslo/polly/wiki/Jenkins import argparse import hashlib import os import shutil import subprocess import sys import tarfile import tempfile import time def clear_except_download(hunter_root): base_dir = os.path.join(hunter_root, '_Base') if os.path.exists(base_dir): print('Clearing directory: {}'.format(base_dir)) hunter_download_dir = os.path.join(base_dir, 'Download', 'Hunter') if os.path.exists(hunter_download_dir): shutil.rmtree(hunter_download_dir) for filename in os.listdir(base_dir): if filename != 'Download': to_remove = os.path.join(base_dir, filename) if os.name == 'nt': # Fix "path too long" error subprocess.check_call(['cmd', '/c', 'rmdir', to_remove, '/S', '/Q']) else: shutil.rmtree(to_remove) def run(): parser = argparse.ArgumentParser("Testing script") parser.add_argument( '--nocreate', action='store_true', help='Do not create Hunter archive (reusing old)' ) parser.add_argument( '--all-release', action='store_true', help='Release build type for all 3rd party packages' ) parser.add_argument( '--clear', action='store_true', help='Remove old testing directories' ) parser.add_argument( '--clear-except-download', action='store_true', help='Remove old testing directories except `Download` directory' ) parser.add_argument( '--disable-builds', action='store_true', help='Disable building of package (useful for checking package can be loaded from cache)' ) parser.add_argument( '--upload', action='store_true', help='Upload cache to server and run checks (clean up will be triggered, same as --clear-except-download)' ) parsed_args = parser.parse_args() if parsed_args.upload: password = os.getenv('GITHUB_USER_PASSWORD') if password is None: sys.exit('Expected environment variable GITHUB_USER_PASSWORD on uploading') cdir = os.getcwd() hunter_root = cdir toolchain = os.getenv('TOOLCHAIN') if not toolchain: sys.exit('Environment variable TOOLCHAIN is empty') project_dir = os.getenv('PROJECT_DIR') if not project_dir: sys.exit('Expected environment variable PROJECT_DIR') ci = os.getenv('TRAVIS') or os.getenv('APPVEYOR') if (ci and toolchain == 'dummy'): print('Skip build: CI dummy (workaround)') sys.exit(0) verbose = True env_verbose = os.getenv('VERBOSE') if env_verbose: if env_verbose == '0': verbose = False elif env_verbose == '1': verbose = True else: sys.exit( 'Environment variable VERBOSE: expected 0 or 1, got "{}"'.format( env_verbose ) ) project_dir = os.path.join(cdir, project_dir) project_dir = os.path.normpath(project_dir) testing_dir = os.path.join(os.getcwd(), '_testing') if os.path.exists(testing_dir) and parsed_args.clear: print('REMOVING: {}'.format(testing_dir)) shutil.rmtree(testing_dir) os.makedirs(testing_dir, exist_ok=True) if os.name == 'nt': hunter_junctions = os.getenv('HUNTER_JUNCTIONS') if hunter_junctions: temp_dir = tempfile.mkdtemp(dir=hunter_junctions) shutil.rmtree(temp_dir) subprocess.check_output( "cmd /c mklink /J {} {}".format(temp_dir, testing_dir) ) testing_dir = temp_dir hunter_url = os.path.join(testing_dir, 'hunter.tar.gz') if parsed_args.nocreate: if not os.path.exists(hunter_url): sys.exit('Option `--nocreate` but no archive') else: arch = tarfile.open(hunter_url, 'w:gz') arch.add('cmake') arch.add('scripts') arch.close() hunter_sha1 = hashlib.sha1(open(hunter_url, 'rb').read()).hexdigest() hunter_root = os.path.join(testing_dir, 'Hunter') if parsed_args.clear_except_download: clear_except_download(hunter_root) if os.name == 'nt': which = 'where' else: which = 'which' polly_root = os.getenv('POLLY_ROOT') if polly_root: print('Using POLLY_ROOT: {}'.format(polly_root)) build_script = os.path.join(polly_root, 'bin', 'build.py') else: build_script = subprocess.check_output( [which, 'build.py'], universal_newlines=True ).split('\n')[0] if not os.path.exists(build_script): sys.exit('Script not found: {}'.format(build_script)) print('Testing in: {}'.format(testing_dir)) os.chdir(testing_dir) args = [ sys.executable, build_script, '--clear', '--config', 'Release', '--toolchain', toolchain, '--home', project_dir, '--fwd', 'CMAKE_POLICY_DEFAULT_CMP0069=NEW', 'HUNTER_SUPPRESS_LIST_OF_FILES=ON', 'HUNTER_ROOT={}'.format(hunter_root), 'TESTING_URL={}'.format(hunter_url), 'TESTING_SHA1={}'.format(hunter_sha1) ] if not parsed_args.nocreate: args += ['HUNTER_RUN_INSTALL=ON'] if parsed_args.disable_builds: args += ['HUNTER_DISABLE_BUILDS=ON'] if parsed_args.all_release: args += ['HUNTER_CONFIGURATION_TYPES=Release'] if parsed_args.upload: passwords = os.path.join( cdir, 'maintenance', 'upload-password-template.cmake' ) args += ['HUNTER_RUN_UPLOAD=ON'] args += ['HUNTER_PASSWORDS_PATH={}'.format(passwords)] args += ['--verbose'] if not verbose: args += ['--discard', '10'] args += ['--tail', '200'] print('Execute command: [') for i in args: print(' `{}`'.format(i)) print(']') subprocess.check_call(args) if parsed_args.upload: seconds = 60 print( 'Wait for GitHub changes became visible ({} seconds)...'.format(seconds) ) time.sleep(seconds) print('Run sanity build') clear_except_download(hunter_root) # Sanity check - run build again with disabled building from sources args = [ sys.executable, build_script, '--clear', '--verbose', '--config', 'Release', '--toolchain', toolchain, '--home', project_dir, '--fwd', 'HUNTER_DISABLE_BUILDS=ON', 'HUNTER_USE_CACHE_SERVERS=ONLY', 'CMAKE_POLICY_DEFAULT_CMP0069=NEW', 'HUNTER_SUPPRESS_LIST_OF_FILES=ON', 'HUNTER_ROOT={}'.format(hunter_root), 'TESTING_URL={}'.format(hunter_url), 'TESTING_SHA1={}'.format(hunter_sha1) ] if not verbose: args += ['--discard', '10'] args += ['--tail', '200'] print('Execute command: [') for i in args: print(' `{}`'.format(i)) print(']') subprocess.check_call(args) if __name__ == "__main__": run() ================================================ FILE: scripts/ShowPredefined.cpp ================================================ // This file generated automatically by `create-predefined-list.py` script. // * https://github.com/ruslo/hunter #define HUNTER_QUOTE(x) #x #define HUNTER_STRING(x) HUNTER_QUOTE(x) #define HUNTER_INFO(x) \ "__HUNTER_MACRO_CHECK_BEGIN__" \ "#define " #x " " HUNTER_STRING(x) \ "__HUNTER_MACRO_CHECK_END__" #include // Check std library version #if defined(__ANDROID__) # include // Header with __ANDROID_API__ #endif #if defined(_MSC_VER) # include // Header with _WIN32_WINNT #endif #if defined(_ABI64) # pragma message(HUNTER_INFO(_ABI64)) #endif #if defined(_ABIO32) # pragma message(HUNTER_INFO(_ABIO32)) #endif #if defined(_AIX) # pragma message(HUNTER_INFO(_AIX)) #endif #if defined(_AIX3) # pragma message(HUNTER_INFO(_AIX3)) #endif #if defined(_AIX32) # pragma message(HUNTER_INFO(_AIX32)) #endif #if defined(_AIX41) # pragma message(HUNTER_INFO(_AIX41)) #endif #if defined(_AIX43) # pragma message(HUNTER_INFO(_AIX43)) #endif #if defined(_ARCH_601) # pragma message(HUNTER_INFO(_ARCH_601)) #endif #if defined(_ARCH_603) # pragma message(HUNTER_INFO(_ARCH_603)) #endif #if defined(_ARCH_PPC) # pragma message(HUNTER_INFO(_ARCH_PPC)) #endif #if defined(_ARCH_PWR) # pragma message(HUNTER_INFO(_ARCH_PWR)) #endif #if defined(_ARCH_PWR2) # pragma message(HUNTER_INFO(_ARCH_PWR2)) #endif #if defined(_ATL_VER) # pragma message(HUNTER_INFO(_ATL_VER)) #endif #if defined(_BIG_ENDIAN) # pragma message(HUNTER_INFO(_BIG_ENDIAN)) #endif #if defined(_BYTE_ORDER) # pragma message(HUNTER_INFO(_BYTE_ORDER)) #endif #if defined(_CHAR_UNSIGNED) # pragma message(HUNTER_INFO(_CHAR_UNSIGNED)) #endif #if defined(_COMPILER_VERSION) # pragma message(HUNTER_INFO(_COMPILER_VERSION)) #endif #if defined(_CONTROL_FLOW_GUARD) # pragma message(HUNTER_INFO(_CONTROL_FLOW_GUARD)) #endif #if defined(_CPPLIB_VER) # pragma message(HUNTER_INFO(_CPPLIB_VER)) #endif #if defined(_CPPRTTI) # pragma message(HUNTER_INFO(_CPPRTTI)) #endif #if defined(_CPPUNWIND) # pragma message(HUNTER_INFO(_CPPUNWIND)) #endif #if defined(_DLL) # pragma message(HUNTER_INFO(_DLL)) #endif #if defined(_GLIBCXX_USE_CXX11_ABI) # pragma message(HUNTER_INFO(_GLIBCXX_USE_CXX11_ABI)) #endif #if defined(_GNU_SOURCE) # pragma message(HUNTER_INFO(_GNU_SOURCE)) #endif #if defined(_IA64) # pragma message(HUNTER_INFO(_IA64)) #endif #if defined(_IBMR2) # pragma message(HUNTER_INFO(_IBMR2)) #endif #if defined(_INTEGRAL_MAX_BITS) # pragma message(HUNTER_INFO(_INTEGRAL_MAX_BITS)) #endif #if defined(_ISO_VOLATILE) # pragma message(HUNTER_INFO(_ISO_VOLATILE)) #endif #if defined(_KERNEL_MODE) # pragma message(HUNTER_INFO(_KERNEL_MODE)) #endif #if defined(_LIBCPP_HAS_NO_ASAN) # pragma message(HUNTER_INFO(_LIBCPP_HAS_NO_ASAN)) #endif #if defined(_LIBCPP_VERSION) # pragma message(HUNTER_INFO(_LIBCPP_VERSION)) #endif #if defined(_LITTLE_ENDIAN) # pragma message(HUNTER_INFO(_LITTLE_ENDIAN)) #endif #if defined(_LP64) # pragma message(HUNTER_INFO(_LP64)) #endif #if defined(_MANAGED) # pragma message(HUNTER_INFO(_MANAGED)) #endif #if defined(_MFC_VER) # pragma message(HUNTER_INFO(_MFC_VER)) #endif #if defined(_MIPSEB) # pragma message(HUNTER_INFO(_MIPSEB)) #endif #if defined(_MIPSEL) # pragma message(HUNTER_INFO(_MIPSEL)) #endif #if defined(_MIPS_ISA_MIPS1) # pragma message(HUNTER_INFO(_MIPS_ISA_MIPS1)) #endif #if defined(_MIPS_ISA_MIPS2) # pragma message(HUNTER_INFO(_MIPS_ISA_MIPS2)) #endif #if defined(_MIPS_ISA_MIPS3) # pragma message(HUNTER_INFO(_MIPS_ISA_MIPS3)) #endif #if defined(_MIPS_ISA_MIPS4) # pragma message(HUNTER_INFO(_MIPS_ISA_MIPS4)) #endif #if defined(_MRI) # pragma message(HUNTER_INFO(_MRI)) #endif #if defined(_MSC_BUILD) # pragma message(HUNTER_INFO(_MSC_BUILD)) #endif #if defined(_MSC_EXTENSIONS) # pragma message(HUNTER_INFO(_MSC_EXTENSIONS)) #endif #if defined(_MSC_FULL_VER) # pragma message(HUNTER_INFO(_MSC_FULL_VER)) #endif #if defined(_MSC_VER) # pragma message(HUNTER_INFO(_MSC_VER)) #endif #if defined(_MSVC_LANG) # pragma message(HUNTER_INFO(_MSVC_LANG)) #endif #if defined(_MT) # pragma message(HUNTER_INFO(_MT)) #endif #if defined(_M_ALPHA) # pragma message(HUNTER_INFO(_M_ALPHA)) #endif #if defined(_M_AMD64) # pragma message(HUNTER_INFO(_M_AMD64)) #endif #if defined(_M_ARM) # pragma message(HUNTER_INFO(_M_ARM)) #endif #if defined(_M_ARM64) # pragma message(HUNTER_INFO(_M_ARM64)) #endif #if defined(_M_ARM_ARMV7VE) # pragma message(HUNTER_INFO(_M_ARM_ARMV7VE)) #endif #if defined(_M_ARM_FP) # pragma message(HUNTER_INFO(_M_ARM_FP)) #endif #if defined(_M_CEE) # pragma message(HUNTER_INFO(_M_CEE)) #endif #if defined(_M_CEE_PURE) # pragma message(HUNTER_INFO(_M_CEE_PURE)) #endif #if defined(_M_CEE_SAFE) # pragma message(HUNTER_INFO(_M_CEE_SAFE)) #endif #if defined(_M_FP_EXCEPT) # pragma message(HUNTER_INFO(_M_FP_EXCEPT)) #endif #if defined(_M_FP_FAST) # pragma message(HUNTER_INFO(_M_FP_FAST)) #endif #if defined(_M_FP_PRECISE) # pragma message(HUNTER_INFO(_M_FP_PRECISE)) #endif #if defined(_M_FP_STRICT) # pragma message(HUNTER_INFO(_M_FP_STRICT)) #endif #if defined(_M_IA64) # pragma message(HUNTER_INFO(_M_IA64)) #endif #if defined(_M_IX86) # pragma message(HUNTER_INFO(_M_IX86)) #endif #if defined(_M_IX86_FP) # pragma message(HUNTER_INFO(_M_IX86_FP)) #endif #if defined(_M_PPC) # pragma message(HUNTER_INFO(_M_PPC)) #endif #if defined(_M_X64) # pragma message(HUNTER_INFO(_M_X64)) #endif #if defined(_NATIVE_WCHAR_T_DEFINED) # pragma message(HUNTER_INFO(_NATIVE_WCHAR_T_DEFINED)) #endif #if defined(_NTO_VERSION) # pragma message(HUNTER_INFO(_NTO_VERSION)) #endif #if defined(_OPENMP) # pragma message(HUNTER_INFO(_OPENMP)) #endif #if defined(_PACC_VER) # pragma message(HUNTER_INFO(_PACC_VER)) #endif #if defined(_PA_RISC1_0) # pragma message(HUNTER_INFO(_PA_RISC1_0)) #endif #if defined(_PA_RISC1_1) # pragma message(HUNTER_INFO(_PA_RISC1_1)) #endif #if defined(_PA_RISC2_0) # pragma message(HUNTER_INFO(_PA_RISC2_0)) #endif #if defined(_PDP_ENDIAN) # pragma message(HUNTER_INFO(_PDP_ENDIAN)) #endif #if defined(_POSIX_SOURCE) # pragma message(HUNTER_INFO(_POSIX_SOURCE)) #endif #if defined(_POWER) # pragma message(HUNTER_INFO(_POWER)) #endif #if defined(_PREFAST_) # pragma message(HUNTER_INFO(_PREFAST_)) #endif #if defined(_R3000) # pragma message(HUNTER_INFO(_R3000)) #endif #if defined(_R4000) # pragma message(HUNTER_INFO(_R4000)) #endif #if defined(_RWSTD_VER) # pragma message(HUNTER_INFO(_RWSTD_VER)) #endif #if defined(_SGI_COMPILER_VERSION) # pragma message(HUNTER_INFO(_SGI_COMPILER_VERSION)) #endif #if defined(_STDC_PREDEF_H) # pragma message(HUNTER_INFO(_STDC_PREDEF_H)) #endif #if defined(_STLPORT_MAJOR) # pragma message(HUNTER_INFO(_STLPORT_MAJOR)) #endif #if defined(_STLPORT_VERSION) # pragma message(HUNTER_INFO(_STLPORT_VERSION)) #endif #if defined(_SYSTYPE_BSD) # pragma message(HUNTER_INFO(_SYSTYPE_BSD)) #endif #if defined(_SYSTYPE_SVR4) # pragma message(HUNTER_INFO(_SYSTYPE_SVR4)) #endif #if defined(_VC_NODEFAULTLIB) # pragma message(HUNTER_INFO(_VC_NODEFAULTLIB)) #endif #if defined(_WCHAR_T_DEFINED) # pragma message(HUNTER_INFO(_WCHAR_T_DEFINED)) #endif #if defined(_WIN32) # pragma message(HUNTER_INFO(_WIN32)) #endif #if defined(_WIN32_WINNT) # pragma message(HUNTER_INFO(_WIN32_WINNT)) #endif #if defined(_WIN64) # pragma message(HUNTER_INFO(_WIN64)) #endif #if defined(_WINRT_DLL) # pragma message(HUNTER_INFO(_WINRT_DLL)) #endif #if defined(_Wp64) # pragma message(HUNTER_INFO(_Wp64)) #endif #if defined(_X86_) # pragma message(HUNTER_INFO(_X86_)) #endif #if defined(_XENON) # pragma message(HUNTER_INFO(_XENON)) #endif #if defined(_XOPEN_SOURCE) # pragma message(HUNTER_INFO(_XOPEN_SOURCE)) #endif #if defined(_YVALS) # pragma message(HUNTER_INFO(_YVALS)) #endif #if defined(__370__) # pragma message(HUNTER_INFO(__370__)) #endif #if defined(__AARCH64EB__) # pragma message(HUNTER_INFO(__AARCH64EB__)) #endif #if defined(__AARCH64EL__) # pragma message(HUNTER_INFO(__AARCH64EL__)) #endif #if defined(__AARCH64_SIMD__) # pragma message(HUNTER_INFO(__AARCH64_SIMD__)) #endif #if defined(__ACCUM_EPSILON__) # pragma message(HUNTER_INFO(__ACCUM_EPSILON__)) #endif #if defined(__ACCUM_FBIT__) # pragma message(HUNTER_INFO(__ACCUM_FBIT__)) #endif #if defined(__ACCUM_IBIT__) # pragma message(HUNTER_INFO(__ACCUM_IBIT__)) #endif #if defined(__ACCUM_MAX__) # pragma message(HUNTER_INFO(__ACCUM_MAX__)) #endif #if defined(__ACCUM_MIN__) # pragma message(HUNTER_INFO(__ACCUM_MIN__)) #endif #if defined(__ALTIVEC__) # pragma message(HUNTER_INFO(__ALTIVEC__)) #endif #if defined(__ANDROID_API__) # pragma message(HUNTER_INFO(__ANDROID_API__)) #endif #if defined(__ANDROID__) # pragma message(HUNTER_INFO(__ANDROID__)) #endif #if defined(__APCS_32__) # pragma message(HUNTER_INFO(__APCS_32__)) #endif #if defined(__APPLE_CC__) # pragma message(HUNTER_INFO(__APPLE_CC__)) #endif #if defined(__APPLE__) # pragma message(HUNTER_INFO(__APPLE__)) #endif #if defined(__ARM64_ARCH_8__) # pragma message(HUNTER_INFO(__ARM64_ARCH_8__)) #endif #if defined(__ARMEB__) # pragma message(HUNTER_INFO(__ARMEB__)) #endif #if defined(__ARMEL__) # pragma message(HUNTER_INFO(__ARMEL__)) #endif #if defined(__ARM_32BIT_STATE) # pragma message(HUNTER_INFO(__ARM_32BIT_STATE)) #endif #if defined(__ARM_64BIT_STATE) # pragma message(HUNTER_INFO(__ARM_64BIT_STATE)) #endif #if defined(__ARM_ACLE) # pragma message(HUNTER_INFO(__ARM_ACLE)) #endif #if defined(__ARM_ALIGN_MAX_STACK_PWR) # pragma message(HUNTER_INFO(__ARM_ALIGN_MAX_STACK_PWR)) #endif #if defined(__ARM_ARCH) # pragma message(HUNTER_INFO(__ARM_ARCH)) #endif #if defined(__ARM_ARCH_7A__) # pragma message(HUNTER_INFO(__ARM_ARCH_7A__)) #endif #if defined(__ARM_ARCH_7S__) # pragma message(HUNTER_INFO(__ARM_ARCH_7S__)) #endif #if defined(__ARM_ARCH_EXT_IDIV__) # pragma message(HUNTER_INFO(__ARM_ARCH_EXT_IDIV__)) #endif #if defined(__ARM_ARCH_ISA_A64) # pragma message(HUNTER_INFO(__ARM_ARCH_ISA_A64)) #endif #if defined(__ARM_ARCH_ISA_ARM) # pragma message(HUNTER_INFO(__ARM_ARCH_ISA_ARM)) #endif #if defined(__ARM_ARCH_ISA_THUMB) # pragma message(HUNTER_INFO(__ARM_ARCH_ISA_THUMB)) #endif #if defined(__ARM_ARCH_PROFILE) # pragma message(HUNTER_INFO(__ARM_ARCH_PROFILE)) #endif #if defined(__ARM_ASM_SYNTAX_UNIFIED__) # pragma message(HUNTER_INFO(__ARM_ASM_SYNTAX_UNIFIED__)) #endif #if defined(__ARM_EABI__) # pragma message(HUNTER_INFO(__ARM_EABI__)) #endif #if defined(__ARM_FEATURE_CLZ) # pragma message(HUNTER_INFO(__ARM_FEATURE_CLZ)) #endif #if defined(__ARM_FEATURE_CRYPTO) # pragma message(HUNTER_INFO(__ARM_FEATURE_CRYPTO)) #endif #if defined(__ARM_FEATURE_DIV) # pragma message(HUNTER_INFO(__ARM_FEATURE_DIV)) #endif #if defined(__ARM_FEATURE_DSP) # pragma message(HUNTER_INFO(__ARM_FEATURE_DSP)) #endif #if defined(__ARM_FEATURE_FMA) # pragma message(HUNTER_INFO(__ARM_FEATURE_FMA)) #endif #if defined(__ARM_FEATURE_LDREX) # pragma message(HUNTER_INFO(__ARM_FEATURE_LDREX)) #endif #if defined(__ARM_FEATURE_QBIT) # pragma message(HUNTER_INFO(__ARM_FEATURE_QBIT)) #endif #if defined(__ARM_FEATURE_SAT) # pragma message(HUNTER_INFO(__ARM_FEATURE_SAT)) #endif #if defined(__ARM_FEATURE_SIMD32) # pragma message(HUNTER_INFO(__ARM_FEATURE_SIMD32)) #endif #if defined(__ARM_FEATURE_UNALIGNED) # pragma message(HUNTER_INFO(__ARM_FEATURE_UNALIGNED)) #endif #if defined(__ARM_FP) # pragma message(HUNTER_INFO(__ARM_FP)) #endif #if defined(__ARM_FP16_FORMAT_IEEE) # pragma message(HUNTER_INFO(__ARM_FP16_FORMAT_IEEE)) #endif #if defined(__ARM_NEON) # pragma message(HUNTER_INFO(__ARM_NEON)) #endif #if defined(__ARM_NEON_FP) # pragma message(HUNTER_INFO(__ARM_NEON_FP)) #endif #if defined(__ARM_NEON__) # pragma message(HUNTER_INFO(__ARM_NEON__)) #endif #if defined(__ARM_PCS_AAPCS64) # pragma message(HUNTER_INFO(__ARM_PCS_AAPCS64)) #endif #if defined(__ARM_PCS_VFP) # pragma message(HUNTER_INFO(__ARM_PCS_VFP)) #endif #if defined(__ARM_SIZEOF_MINIMAL_ENUM) # pragma message(HUNTER_INFO(__ARM_SIZEOF_MINIMAL_ENUM)) #endif #if defined(__ARM_SIZEOF_WCHAR_T) # pragma message(HUNTER_INFO(__ARM_SIZEOF_WCHAR_T)) #endif #if defined(__ARM_VFPV3__) # pragma message(HUNTER_INFO(__ARM_VFPV3__)) #endif #if defined(__ARM_VFPV4__) # pragma message(HUNTER_INFO(__ARM_VFPV4__)) #endif #if defined(__ATOMIC_ACQUIRE) # pragma message(HUNTER_INFO(__ATOMIC_ACQUIRE)) #endif #if defined(__ATOMIC_ACQ_REL) # pragma message(HUNTER_INFO(__ATOMIC_ACQ_REL)) #endif #if defined(__ATOMIC_CONSUME) # pragma message(HUNTER_INFO(__ATOMIC_CONSUME)) #endif #if defined(__ATOMIC_HLE_ACQUIRE) # pragma message(HUNTER_INFO(__ATOMIC_HLE_ACQUIRE)) #endif #if defined(__ATOMIC_HLE_RELEASE) # pragma message(HUNTER_INFO(__ATOMIC_HLE_RELEASE)) #endif #if defined(__ATOMIC_RELAXED) # pragma message(HUNTER_INFO(__ATOMIC_RELAXED)) #endif #if defined(__ATOMIC_RELEASE) # pragma message(HUNTER_INFO(__ATOMIC_RELEASE)) #endif #if defined(__ATOMIC_SEQ_CST) # pragma message(HUNTER_INFO(__ATOMIC_SEQ_CST)) #endif #if defined(__ATOM__) # pragma message(HUNTER_INFO(__ATOM__)) #endif #if defined(__AVX2__) # pragma message(HUNTER_INFO(__AVX2__)) #endif #if defined(__AVX__) # pragma message(HUNTER_INFO(__AVX__)) #endif #if defined(__BEOS__) # pragma message(HUNTER_INFO(__BEOS__)) #endif #if defined(__BFIN__) # pragma message(HUNTER_INFO(__BFIN__)) #endif #if defined(__BIGGEST_ALIGNMENT__) # pragma message(HUNTER_INFO(__BIGGEST_ALIGNMENT__)) #endif #if defined(__BIG_ENDIAN) # pragma message(HUNTER_INFO(__BIG_ENDIAN)) #endif #if defined(__BIG_ENDIAN__) # pragma message(HUNTER_INFO(__BIG_ENDIAN__)) #endif #if defined(__BLOCKS__) # pragma message(HUNTER_INFO(__BLOCKS__)) #endif #if defined(__BORLANDC__) # pragma message(HUNTER_INFO(__BORLANDC__)) #endif #if defined(__BYTE_ORDER) # pragma message(HUNTER_INFO(__BYTE_ORDER)) #endif #if defined(__BYTE_ORDER__) # pragma message(HUNTER_INFO(__BYTE_ORDER__)) #endif #if defined(__CHAR16_TYPE__) # pragma message(HUNTER_INFO(__CHAR16_TYPE__)) #endif #if defined(__CHAR32_TYPE__) # pragma message(HUNTER_INFO(__CHAR32_TYPE__)) #endif #if defined(__CHAR_BIT__) # pragma message(HUNTER_INFO(__CHAR_BIT__)) #endif #if defined(__CHAR_UNSIGNED__) # pragma message(HUNTER_INFO(__CHAR_UNSIGNED__)) #endif #if defined(__CLR_VER) # pragma message(HUNTER_INFO(__CLR_VER)) #endif #if defined(__CODEGEARC__) # pragma message(HUNTER_INFO(__CODEGEARC__)) #endif #if defined(__COMO_VERSION__) # pragma message(HUNTER_INFO(__COMO_VERSION__)) #endif #if defined(__COMO__) # pragma message(HUNTER_INFO(__COMO__)) #endif #if defined(__COMPILER_VER__) # pragma message(HUNTER_INFO(__COMPILER_VER__)) #endif #if defined(__CONO_VERSION__) # pragma message(HUNTER_INFO(__CONO_VERSION__)) #endif #if defined(__CONSTANT_CFSTRINGS__) # pragma message(HUNTER_INFO(__CONSTANT_CFSTRINGS__)) #endif #if defined(__COUNTER__) # pragma message(HUNTER_INFO(__COUNTER__)) #endif #if defined(__CRTL_VER) # pragma message(HUNTER_INFO(__CRTL_VER)) #endif #if defined(__CWCC__) # pragma message(HUNTER_INFO(__CWCC__)) #endif #if defined(__CYGWIN__) # pragma message(HUNTER_INFO(__CYGWIN__)) #endif #if defined(__DA_FBIT__) # pragma message(HUNTER_INFO(__DA_FBIT__)) #endif #if defined(__DA_IBIT__) # pragma message(HUNTER_INFO(__DA_IBIT__)) #endif #if defined(__DBL_DECIMAL_DIG__) # pragma message(HUNTER_INFO(__DBL_DECIMAL_DIG__)) #endif #if defined(__DBL_DENORM_MIN__) # pragma message(HUNTER_INFO(__DBL_DENORM_MIN__)) #endif #if defined(__DBL_DIG__) # pragma message(HUNTER_INFO(__DBL_DIG__)) #endif #if defined(__DBL_EPSILON__) # pragma message(HUNTER_INFO(__DBL_EPSILON__)) #endif #if defined(__DBL_HAS_DENORM__) # pragma message(HUNTER_INFO(__DBL_HAS_DENORM__)) #endif #if defined(__DBL_HAS_INFINITY__) # pragma message(HUNTER_INFO(__DBL_HAS_INFINITY__)) #endif #if defined(__DBL_HAS_QUIET_NAN__) # pragma message(HUNTER_INFO(__DBL_HAS_QUIET_NAN__)) #endif #if defined(__DBL_MANT_DIG__) # pragma message(HUNTER_INFO(__DBL_MANT_DIG__)) #endif #if defined(__DBL_MAX_10_EXP__) # pragma message(HUNTER_INFO(__DBL_MAX_10_EXP__)) #endif #if defined(__DBL_MAX_EXP__) # pragma message(HUNTER_INFO(__DBL_MAX_EXP__)) #endif #if defined(__DBL_MAX__) # pragma message(HUNTER_INFO(__DBL_MAX__)) #endif #if defined(__DBL_MIN_10_EXP__) # pragma message(HUNTER_INFO(__DBL_MIN_10_EXP__)) #endif #if defined(__DBL_MIN_EXP__) # pragma message(HUNTER_INFO(__DBL_MIN_EXP__)) #endif #if defined(__DBL_MIN__) # pragma message(HUNTER_INFO(__DBL_MIN__)) #endif #if defined(__DCC__) # pragma message(HUNTER_INFO(__DCC__)) #endif #if defined(__DEC128_EPSILON__) # pragma message(HUNTER_INFO(__DEC128_EPSILON__)) #endif #if defined(__DEC128_MANT_DIG__) # pragma message(HUNTER_INFO(__DEC128_MANT_DIG__)) #endif #if defined(__DEC128_MAX_EXP__) # pragma message(HUNTER_INFO(__DEC128_MAX_EXP__)) #endif #if defined(__DEC128_MAX__) # pragma message(HUNTER_INFO(__DEC128_MAX__)) #endif #if defined(__DEC128_MIN_EXP__) # pragma message(HUNTER_INFO(__DEC128_MIN_EXP__)) #endif #if defined(__DEC128_MIN__) # pragma message(HUNTER_INFO(__DEC128_MIN__)) #endif #if defined(__DEC128_SUBNORMAL_MIN__) # pragma message(HUNTER_INFO(__DEC128_SUBNORMAL_MIN__)) #endif #if defined(__DEC32_EPSILON__) # pragma message(HUNTER_INFO(__DEC32_EPSILON__)) #endif #if defined(__DEC32_MANT_DIG__) # pragma message(HUNTER_INFO(__DEC32_MANT_DIG__)) #endif #if defined(__DEC32_MAX_EXP__) # pragma message(HUNTER_INFO(__DEC32_MAX_EXP__)) #endif #if defined(__DEC32_MAX__) # pragma message(HUNTER_INFO(__DEC32_MAX__)) #endif #if defined(__DEC32_MIN_EXP__) # pragma message(HUNTER_INFO(__DEC32_MIN_EXP__)) #endif #if defined(__DEC32_MIN__) # pragma message(HUNTER_INFO(__DEC32_MIN__)) #endif #if defined(__DEC32_SUBNORMAL_MIN__) # pragma message(HUNTER_INFO(__DEC32_SUBNORMAL_MIN__)) #endif #if defined(__DEC64_EPSILON__) # pragma message(HUNTER_INFO(__DEC64_EPSILON__)) #endif #if defined(__DEC64_MANT_DIG__) # pragma message(HUNTER_INFO(__DEC64_MANT_DIG__)) #endif #if defined(__DEC64_MAX_EXP__) # pragma message(HUNTER_INFO(__DEC64_MAX_EXP__)) #endif #if defined(__DEC64_MAX__) # pragma message(HUNTER_INFO(__DEC64_MAX__)) #endif #if defined(__DEC64_MIN_EXP__) # pragma message(HUNTER_INFO(__DEC64_MIN_EXP__)) #endif #if defined(__DEC64_MIN__) # pragma message(HUNTER_INFO(__DEC64_MIN__)) #endif #if defined(__DEC64_SUBNORMAL_MIN__) # pragma message(HUNTER_INFO(__DEC64_SUBNORMAL_MIN__)) #endif #if defined(__DECC) # pragma message(HUNTER_INFO(__DECC)) #endif #if defined(__DECCXX) # pragma message(HUNTER_INFO(__DECCXX)) #endif #if defined(__DECCXX_VER) # pragma message(HUNTER_INFO(__DECCXX_VER)) #endif #if defined(__DECC_VER) # pragma message(HUNTER_INFO(__DECC_VER)) #endif #if defined(__DECIMAL_BID_FORMAT__) # pragma message(HUNTER_INFO(__DECIMAL_BID_FORMAT__)) #endif #if defined(__DECIMAL_DIG__) # pragma message(HUNTER_INFO(__DECIMAL_DIG__)) #endif #if defined(__DEC_EVAL_METHOD__) # pragma message(HUNTER_INFO(__DEC_EVAL_METHOD__)) #endif #if defined(__DEPRECATED) # pragma message(HUNTER_INFO(__DEPRECATED)) #endif #if defined(__DMC__) # pragma message(HUNTER_INFO(__DMC__)) #endif #if defined(__DQ_FBIT__) # pragma message(HUNTER_INFO(__DQ_FBIT__)) #endif #if defined(__DQ_IBIT__) # pragma message(HUNTER_INFO(__DQ_IBIT__)) #endif #if defined(__DYNAMIC__) # pragma message(HUNTER_INFO(__DYNAMIC__)) #endif #if defined(__DragonFly__) # pragma message(HUNTER_INFO(__DragonFly__)) #endif #if defined(__ECC) # pragma message(HUNTER_INFO(__ECC)) #endif #if defined(__EDG__) # pragma message(HUNTER_INFO(__EDG__)) #endif #if defined(__ELF__) # pragma message(HUNTER_INFO(__ELF__)) #endif #if defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) # pragma message(HUNTER_INFO(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)) #endif #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) # pragma message(HUNTER_INFO(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)) #endif #if defined(__EXCEPTIONS) # pragma message(HUNTER_INFO(__EXCEPTIONS)) #endif #if defined(__FINITE_MATH_ONLY__) # pragma message(HUNTER_INFO(__FINITE_MATH_ONLY__)) #endif #if defined(__FLOAT_WORD_ORDER__) # pragma message(HUNTER_INFO(__FLOAT_WORD_ORDER__)) #endif #if defined(__FLT_DECIMAL_DIG__) # pragma message(HUNTER_INFO(__FLT_DECIMAL_DIG__)) #endif #if defined(__FLT_DENORM_MIN__) # pragma message(HUNTER_INFO(__FLT_DENORM_MIN__)) #endif #if defined(__FLT_DIG__) # pragma message(HUNTER_INFO(__FLT_DIG__)) #endif #if defined(__FLT_EPSILON__) # pragma message(HUNTER_INFO(__FLT_EPSILON__)) #endif #if defined(__FLT_EVAL_METHOD__) # pragma message(HUNTER_INFO(__FLT_EVAL_METHOD__)) #endif #if defined(__FLT_HAS_DENORM__) # pragma message(HUNTER_INFO(__FLT_HAS_DENORM__)) #endif #if defined(__FLT_HAS_INFINITY__) # pragma message(HUNTER_INFO(__FLT_HAS_INFINITY__)) #endif #if defined(__FLT_HAS_QUIET_NAN__) # pragma message(HUNTER_INFO(__FLT_HAS_QUIET_NAN__)) #endif #if defined(__FLT_MANT_DIG__) # pragma message(HUNTER_INFO(__FLT_MANT_DIG__)) #endif #if defined(__FLT_MAX_10_EXP__) # pragma message(HUNTER_INFO(__FLT_MAX_10_EXP__)) #endif #if defined(__FLT_MAX_EXP__) # pragma message(HUNTER_INFO(__FLT_MAX_EXP__)) #endif #if defined(__FLT_MAX__) # pragma message(HUNTER_INFO(__FLT_MAX__)) #endif #if defined(__FLT_MIN_10_EXP__) # pragma message(HUNTER_INFO(__FLT_MIN_10_EXP__)) #endif #if defined(__FLT_MIN_EXP__) # pragma message(HUNTER_INFO(__FLT_MIN_EXP__)) #endif #if defined(__FLT_MIN__) # pragma message(HUNTER_INFO(__FLT_MIN__)) #endif #if defined(__FLT_RADIX__) # pragma message(HUNTER_INFO(__FLT_RADIX__)) #endif #if defined(__FMA4__) # pragma message(HUNTER_INFO(__FMA4__)) #endif #if defined(__FMA__) # pragma message(HUNTER_INFO(__FMA__)) #endif #if defined(__FP_FAST_FMA) # pragma message(HUNTER_INFO(__FP_FAST_FMA)) #endif #if defined(__FP_FAST_FMAF) # pragma message(HUNTER_INFO(__FP_FAST_FMAF)) #endif #if defined(__FP_FAST_FMAL) # pragma message(HUNTER_INFO(__FP_FAST_FMAL)) #endif #if defined(__FRACT_EPSILON__) # pragma message(HUNTER_INFO(__FRACT_EPSILON__)) #endif #if defined(__FRACT_FBIT__) # pragma message(HUNTER_INFO(__FRACT_FBIT__)) #endif #if defined(__FRACT_IBIT__) # pragma message(HUNTER_INFO(__FRACT_IBIT__)) #endif #if defined(__FRACT_MAX__) # pragma message(HUNTER_INFO(__FRACT_MAX__)) #endif #if defined(__FRACT_MIN__) # pragma message(HUNTER_INFO(__FRACT_MIN__)) #endif #if defined(__FUNCDNAME__) # pragma message(HUNTER_INFO(__FUNCDNAME__)) #endif #if defined(__FUNCSIG__) # pragma message(HUNTER_INFO(__FUNCSIG__)) #endif #if defined(__FXSR__) # pragma message(HUNTER_INFO(__FXSR__)) #endif #if defined(__FreeBSD__) # pragma message(HUNTER_INFO(__FreeBSD__)) #endif #if defined(__FreeBSD_version) # pragma message(HUNTER_INFO(__FreeBSD_version)) #endif #if defined(__GCCXML__) # pragma message(HUNTER_INFO(__GCCXML__)) #endif #if defined(__GCC_ASM_FLAG_OUTPUTS__) # pragma message(HUNTER_INFO(__GCC_ASM_FLAG_OUTPUTS__)) #endif #if defined(__GCC_ATOMIC_BOOL_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_BOOL_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_CHAR16_T_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_CHAR16_T_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_CHAR32_T_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_CHAR32_T_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_CHAR_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_CHAR_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_INT_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_INT_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_LLONG_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_LLONG_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_LONG_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_LONG_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_POINTER_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_POINTER_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_SHORT_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_SHORT_LOCK_FREE)) #endif #if defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL) # pragma message(HUNTER_INFO(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL)) #endif #if defined(__GCC_ATOMIC_WCHAR_T_LOCK_FREE) # pragma message(HUNTER_INFO(__GCC_ATOMIC_WCHAR_T_LOCK_FREE)) #endif #if defined(__GCC_HAVE_DWARF2_CFI_ASM) # pragma message(HUNTER_INFO(__GCC_HAVE_DWARF2_CFI_ASM)) #endif #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) # pragma message(HUNTER_INFO(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)) #endif #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) # pragma message(HUNTER_INFO(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)) #endif #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) # pragma message(HUNTER_INFO(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)) #endif #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) # pragma message(HUNTER_INFO(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) #endif #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) # pragma message(HUNTER_INFO(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) #endif #if defined(__GCC_IEC_559) # pragma message(HUNTER_INFO(__GCC_IEC_559)) #endif #if defined(__GCC_IEC_559_COMPLEX) # pragma message(HUNTER_INFO(__GCC_IEC_559_COMPLEX)) #endif #if defined(__GHS_VERSION_NUMBER__) # pragma message(HUNTER_INFO(__GHS_VERSION_NUMBER__)) #endif #if defined(__GLIBCPP__) # pragma message(HUNTER_INFO(__GLIBCPP__)) #endif #if defined(__GLIBCXX_BITSIZE_INT_N_0) # pragma message(HUNTER_INFO(__GLIBCXX_BITSIZE_INT_N_0)) #endif #if defined(__GLIBCXX_TYPE_INT_N_0) # pragma message(HUNTER_INFO(__GLIBCXX_TYPE_INT_N_0)) #endif #if defined(__GLIBCXX__) # pragma message(HUNTER_INFO(__GLIBCXX__)) #endif #if defined(__GLIBC__) # pragma message(HUNTER_INFO(__GLIBC__)) #endif #if defined(__GNUC_GNU_INLINE__) # pragma message(HUNTER_INFO(__GNUC_GNU_INLINE__)) #endif #if defined(__GNUC_LIBSTD_MINOR__) # pragma message(HUNTER_INFO(__GNUC_LIBSTD_MINOR__)) #endif #if defined(__GNUC_LIBSTD__) # pragma message(HUNTER_INFO(__GNUC_LIBSTD__)) #endif #if defined(__GNUC_MINOR__) # pragma message(HUNTER_INFO(__GNUC_MINOR__)) #endif #if defined(__GNUC_PATCHLEVEL__) # pragma message(HUNTER_INFO(__GNUC_PATCHLEVEL__)) #endif #if defined(__GNUC_STDC_INLINE__) # pragma message(HUNTER_INFO(__GNUC_STDC_INLINE__)) #endif #if defined(__GNUC__) # pragma message(HUNTER_INFO(__GNUC__)) #endif #if defined(__GNUG__) # pragma message(HUNTER_INFO(__GNUG__)) #endif #if defined(__GNU_LIBRARY__) # pragma message(HUNTER_INFO(__GNU_LIBRARY__)) #endif #if defined(__GXX_ABI_VERSION) # pragma message(HUNTER_INFO(__GXX_ABI_VERSION)) #endif #if defined(__GXX_EXPERIMENTAL_CXX0X__) # pragma message(HUNTER_INFO(__GXX_EXPERIMENTAL_CXX0X__)) #endif #if defined(__GXX_RTTI) # pragma message(HUNTER_INFO(__GXX_RTTI)) #endif #if defined(__GXX_TYPEINFO_EQUALITY_INLINE) # pragma message(HUNTER_INFO(__GXX_TYPEINFO_EQUALITY_INLINE)) #endif #if defined(__GXX_WEAK__) # pragma message(HUNTER_INFO(__GXX_WEAK__)) #endif #if defined(__HAIKU__) # pragma message(HUNTER_INFO(__HAIKU__)) #endif #if defined(__HA_FBIT__) # pragma message(HUNTER_INFO(__HA_FBIT__)) #endif #if defined(__HA_IBIT__) # pragma message(HUNTER_INFO(__HA_IBIT__)) #endif #if defined(__HIGHC__) # pragma message(HUNTER_INFO(__HIGHC__)) #endif #if defined(__HPPA11__) # pragma message(HUNTER_INFO(__HPPA11__)) #endif #if defined(__HPPA20__) # pragma message(HUNTER_INFO(__HPPA20__)) #endif #if defined(__HPPA__) # pragma message(HUNTER_INFO(__HPPA__)) #endif #if defined(__HP_aCC) # pragma message(HUNTER_INFO(__HP_aCC)) #endif #if defined(__HQ_FBIT__) # pragma message(HUNTER_INFO(__HQ_FBIT__)) #endif #if defined(__HQ_IBIT__) # pragma message(HUNTER_INFO(__HQ_IBIT__)) #endif #if defined(__I86__) # pragma message(HUNTER_INFO(__I86__)) #endif #if defined(__IA64__) # pragma message(HUNTER_INFO(__IA64__)) #endif #if defined(__IAR_SYSTEMS_ICC__) # pragma message(HUNTER_INFO(__IAR_SYSTEMS_ICC__)) #endif #if defined(__IBMCPP__) # pragma message(HUNTER_INFO(__IBMCPP__)) #endif #if defined(__ICC) # pragma message(HUNTER_INFO(__ICC)) #endif #if defined(__ICL) # pragma message(HUNTER_INFO(__ICL)) #endif #if defined(__INT16_C) # pragma message(HUNTER_INFO(__INT16_C)) #endif #if defined(__INT16_MAX__) # pragma message(HUNTER_INFO(__INT16_MAX__)) #endif #if defined(__INT16_TYPE__) # pragma message(HUNTER_INFO(__INT16_TYPE__)) #endif #if defined(__INT32_C) # pragma message(HUNTER_INFO(__INT32_C)) #endif #if defined(__INT32_MAX__) # pragma message(HUNTER_INFO(__INT32_MAX__)) #endif #if defined(__INT32_TYPE__) # pragma message(HUNTER_INFO(__INT32_TYPE__)) #endif #if defined(__INT64_C) # pragma message(HUNTER_INFO(__INT64_C)) #endif #if defined(__INT64_C_SUFFIX__) # pragma message(HUNTER_INFO(__INT64_C_SUFFIX__)) #endif #if defined(__INT64_MAX__) # pragma message(HUNTER_INFO(__INT64_MAX__)) #endif #if defined(__INT64_TYPE__) # pragma message(HUNTER_INFO(__INT64_TYPE__)) #endif #if defined(__INT8_C) # pragma message(HUNTER_INFO(__INT8_C)) #endif #if defined(__INT8_MAX__) # pragma message(HUNTER_INFO(__INT8_MAX__)) #endif #if defined(__INT8_TYPE__) # pragma message(HUNTER_INFO(__INT8_TYPE__)) #endif #if defined(__INTEL_COMPILER) # pragma message(HUNTER_INFO(__INTEL_COMPILER)) #endif #if defined(__INTEL__) # pragma message(HUNTER_INFO(__INTEL__)) #endif #if defined(__INTMAX_C) # pragma message(HUNTER_INFO(__INTMAX_C)) #endif #if defined(__INTMAX_MAX__) # pragma message(HUNTER_INFO(__INTMAX_MAX__)) #endif #if defined(__INTMAX_TYPE__) # pragma message(HUNTER_INFO(__INTMAX_TYPE__)) #endif #if defined(__INTMAX_WIDTH__) # pragma message(HUNTER_INFO(__INTMAX_WIDTH__)) #endif #if defined(__INTPTR_MAX__) # pragma message(HUNTER_INFO(__INTPTR_MAX__)) #endif #if defined(__INTPTR_TYPE__) # pragma message(HUNTER_INFO(__INTPTR_TYPE__)) #endif #if defined(__INTPTR_WIDTH__) # pragma message(HUNTER_INFO(__INTPTR_WIDTH__)) #endif #if defined(__INT_FAST16_MAX__) # pragma message(HUNTER_INFO(__INT_FAST16_MAX__)) #endif #if defined(__INT_FAST16_TYPE__) # pragma message(HUNTER_INFO(__INT_FAST16_TYPE__)) #endif #if defined(__INT_FAST32_MAX__) # pragma message(HUNTER_INFO(__INT_FAST32_MAX__)) #endif #if defined(__INT_FAST32_TYPE__) # pragma message(HUNTER_INFO(__INT_FAST32_TYPE__)) #endif #if defined(__INT_FAST64_MAX__) # pragma message(HUNTER_INFO(__INT_FAST64_MAX__)) #endif #if defined(__INT_FAST64_TYPE__) # pragma message(HUNTER_INFO(__INT_FAST64_TYPE__)) #endif #if defined(__INT_FAST8_MAX__) # pragma message(HUNTER_INFO(__INT_FAST8_MAX__)) #endif #if defined(__INT_FAST8_TYPE__) # pragma message(HUNTER_INFO(__INT_FAST8_TYPE__)) #endif #if defined(__INT_LEAST16_MAX__) # pragma message(HUNTER_INFO(__INT_LEAST16_MAX__)) #endif #if defined(__INT_LEAST16_TYPE__) # pragma message(HUNTER_INFO(__INT_LEAST16_TYPE__)) #endif #if defined(__INT_LEAST32_MAX__) # pragma message(HUNTER_INFO(__INT_LEAST32_MAX__)) #endif #if defined(__INT_LEAST32_TYPE__) # pragma message(HUNTER_INFO(__INT_LEAST32_TYPE__)) #endif #if defined(__INT_LEAST64_MAX__) # pragma message(HUNTER_INFO(__INT_LEAST64_MAX__)) #endif #if defined(__INT_LEAST64_TYPE__) # pragma message(HUNTER_INFO(__INT_LEAST64_TYPE__)) #endif #if defined(__INT_LEAST8_MAX__) # pragma message(HUNTER_INFO(__INT_LEAST8_MAX__)) #endif #if defined(__INT_LEAST8_TYPE__) # pragma message(HUNTER_INFO(__INT_LEAST8_TYPE__)) #endif #if defined(__INT_MAX__) # pragma message(HUNTER_INFO(__INT_MAX__)) #endif #if defined(__KCC) # pragma message(HUNTER_INFO(__KCC)) #endif #if defined(__LACCUM_EPSILON__) # pragma message(HUNTER_INFO(__LACCUM_EPSILON__)) #endif #if defined(__LACCUM_FBIT__) # pragma message(HUNTER_INFO(__LACCUM_FBIT__)) #endif #if defined(__LACCUM_IBIT__) # pragma message(HUNTER_INFO(__LACCUM_IBIT__)) #endif #if defined(__LACCUM_MAX__) # pragma message(HUNTER_INFO(__LACCUM_MAX__)) #endif #if defined(__LACCUM_MIN__) # pragma message(HUNTER_INFO(__LACCUM_MIN__)) #endif #if defined(__LDBL_DENORM_MIN__) # pragma message(HUNTER_INFO(__LDBL_DENORM_MIN__)) #endif #if defined(__LDBL_DIG__) # pragma message(HUNTER_INFO(__LDBL_DIG__)) #endif #if defined(__LDBL_EPSILON__) # pragma message(HUNTER_INFO(__LDBL_EPSILON__)) #endif #if defined(__LDBL_HAS_DENORM__) # pragma message(HUNTER_INFO(__LDBL_HAS_DENORM__)) #endif #if defined(__LDBL_HAS_INFINITY__) # pragma message(HUNTER_INFO(__LDBL_HAS_INFINITY__)) #endif #if defined(__LDBL_HAS_QUIET_NAN__) # pragma message(HUNTER_INFO(__LDBL_HAS_QUIET_NAN__)) #endif #if defined(__LDBL_MANT_DIG__) # pragma message(HUNTER_INFO(__LDBL_MANT_DIG__)) #endif #if defined(__LDBL_MAX_10_EXP__) # pragma message(HUNTER_INFO(__LDBL_MAX_10_EXP__)) #endif #if defined(__LDBL_MAX_EXP__) # pragma message(HUNTER_INFO(__LDBL_MAX_EXP__)) #endif #if defined(__LDBL_MAX__) # pragma message(HUNTER_INFO(__LDBL_MAX__)) #endif #if defined(__LDBL_MIN_10_EXP__) # pragma message(HUNTER_INFO(__LDBL_MIN_10_EXP__)) #endif #if defined(__LDBL_MIN_EXP__) # pragma message(HUNTER_INFO(__LDBL_MIN_EXP__)) #endif #if defined(__LDBL_MIN__) # pragma message(HUNTER_INFO(__LDBL_MIN__)) #endif #if defined(__LFRACT_EPSILON__) # pragma message(HUNTER_INFO(__LFRACT_EPSILON__)) #endif #if defined(__LFRACT_FBIT__) # pragma message(HUNTER_INFO(__LFRACT_FBIT__)) #endif #if defined(__LFRACT_IBIT__) # pragma message(HUNTER_INFO(__LFRACT_IBIT__)) #endif #if defined(__LFRACT_MAX__) # pragma message(HUNTER_INFO(__LFRACT_MAX__)) #endif #if defined(__LFRACT_MIN__) # pragma message(HUNTER_INFO(__LFRACT_MIN__)) #endif #if defined(__LIBCOMO__) # pragma message(HUNTER_INFO(__LIBCOMO__)) #endif #if defined(__LIBREL__) # pragma message(HUNTER_INFO(__LIBREL__)) #endif #if defined(__LITTLE_ENDIAN) # pragma message(HUNTER_INFO(__LITTLE_ENDIAN)) #endif #if defined(__LITTLE_ENDIAN__) # pragma message(HUNTER_INFO(__LITTLE_ENDIAN__)) #endif #if defined(__LLACCUM_EPSILON__) # pragma message(HUNTER_INFO(__LLACCUM_EPSILON__)) #endif #if defined(__LLACCUM_FBIT__) # pragma message(HUNTER_INFO(__LLACCUM_FBIT__)) #endif #if defined(__LLACCUM_IBIT__) # pragma message(HUNTER_INFO(__LLACCUM_IBIT__)) #endif #if defined(__LLACCUM_MAX__) # pragma message(HUNTER_INFO(__LLACCUM_MAX__)) #endif #if defined(__LLACCUM_MIN__) # pragma message(HUNTER_INFO(__LLACCUM_MIN__)) #endif #if defined(__LLFRACT_EPSILON__) # pragma message(HUNTER_INFO(__LLFRACT_EPSILON__)) #endif #if defined(__LLFRACT_FBIT__) # pragma message(HUNTER_INFO(__LLFRACT_FBIT__)) #endif #if defined(__LLFRACT_IBIT__) # pragma message(HUNTER_INFO(__LLFRACT_IBIT__)) #endif #if defined(__LLFRACT_MAX__) # pragma message(HUNTER_INFO(__LLFRACT_MAX__)) #endif #if defined(__LLFRACT_MIN__) # pragma message(HUNTER_INFO(__LLFRACT_MIN__)) #endif #if defined(__LONG_LONG_MAX__) # pragma message(HUNTER_INFO(__LONG_LONG_MAX__)) #endif #if defined(__LONG_MAX__) # pragma message(HUNTER_INFO(__LONG_MAX__)) #endif #if defined(__LP64__) # pragma message(HUNTER_INFO(__LP64__)) #endif #if defined(__MACH__) # pragma message(HUNTER_INFO(__MACH__)) #endif #if defined(__MIC__) # pragma message(HUNTER_INFO(__MIC__)) #endif #if defined(__MINGW32_VERSION_MAJOR) # pragma message(HUNTER_INFO(__MINGW32_VERSION_MAJOR)) #endif #if defined(__MINGW32_VERSION_MINOR) # pragma message(HUNTER_INFO(__MINGW32_VERSION_MINOR)) #endif #if defined(__MINGW32__) # pragma message(HUNTER_INFO(__MINGW32__)) #endif #if defined(__MINGW64_VERSION_MAJOR) # pragma message(HUNTER_INFO(__MINGW64_VERSION_MAJOR)) #endif #if defined(__MINGW64_VERSION_MINOR) # pragma message(HUNTER_INFO(__MINGW64_VERSION_MINOR)) #endif #if defined(__MINGW64__) # pragma message(HUNTER_INFO(__MINGW64__)) #endif #if defined(__MIPSEB) # pragma message(HUNTER_INFO(__MIPSEB)) #endif #if defined(__MIPSEB__) # pragma message(HUNTER_INFO(__MIPSEB__)) #endif #if defined(__MIPSEL) # pragma message(HUNTER_INFO(__MIPSEL)) #endif #if defined(__MIPSEL__) # pragma message(HUNTER_INFO(__MIPSEL__)) #endif #if defined(__MIPS_ISA2__) # pragma message(HUNTER_INFO(__MIPS_ISA2__)) #endif #if defined(__MIPS_ISA3__) # pragma message(HUNTER_INFO(__MIPS_ISA3__)) #endif #if defined(__MIPS_ISA4__) # pragma message(HUNTER_INFO(__MIPS_ISA4__)) #endif #if defined(__MIPS__) # pragma message(HUNTER_INFO(__MIPS__)) #endif #if defined(__MMX__) # pragma message(HUNTER_INFO(__MMX__)) #endif #if defined(__MRC__) # pragma message(HUNTER_INFO(__MRC__)) #endif #if defined(__MSIPL_COMPILE_H) # pragma message(HUNTER_INFO(__MSIPL_COMPILE_H)) #endif #if defined(__MSL_CPP__) # pragma message(HUNTER_INFO(__MSL_CPP__)) #endif #if defined(__MSL__) # pragma message(HUNTER_INFO(__MSL__)) #endif #if defined(__MSVC_RUNTIME_CHECKS) # pragma message(HUNTER_INFO(__MSVC_RUNTIME_CHECKS)) #endif #if defined(__MWERKS__) # pragma message(HUNTER_INFO(__MWERKS__)) #endif #if defined(__NETBSD__) # pragma message(HUNTER_INFO(__NETBSD__)) #endif #if defined(__NETBSD_version) # pragma message(HUNTER_INFO(__NETBSD_version)) #endif #if defined(__NO_MATH_INLINES) # pragma message(HUNTER_INFO(__NO_MATH_INLINES)) #endif #if defined(__NetBSD_Version) # pragma message(HUNTER_INFO(__NetBSD_Version)) #endif #if defined(__NetBSD__) # pragma message(HUNTER_INFO(__NetBSD__)) #endif #if defined(__OBJC__) # pragma message(HUNTER_INFO(__OBJC__)) #endif #if defined(__ORDER_BIG_ENDIAN__) # pragma message(HUNTER_INFO(__ORDER_BIG_ENDIAN__)) #endif #if defined(__ORDER_LITTLE_ENDIAN__) # pragma message(HUNTER_INFO(__ORDER_LITTLE_ENDIAN__)) #endif #if defined(__ORDER_PDP_ENDIAN__) # pragma message(HUNTER_INFO(__ORDER_PDP_ENDIAN__)) #endif #if defined(__OS400__) # pragma message(HUNTER_INFO(__OS400__)) #endif #if defined(__OpenBSD__) # pragma message(HUNTER_INFO(__OpenBSD__)) #endif #if defined(__PA7100__) # pragma message(HUNTER_INFO(__PA7100__)) #endif #if defined(__PA8000__) # pragma message(HUNTER_INFO(__PA8000__)) #endif #if defined(__PATHCC__) # pragma message(HUNTER_INFO(__PATHCC__)) #endif #if defined(__PDP_ENDIAN) # pragma message(HUNTER_INFO(__PDP_ENDIAN)) #endif #if defined(__PGI) # pragma message(HUNTER_INFO(__PGI)) #endif #if defined(__PGIC_MINOR__) # pragma message(HUNTER_INFO(__PGIC_MINOR__)) #endif #if defined(__PGIC_PATCHLEVEL__) # pragma message(HUNTER_INFO(__PGIC_PATCHLEVEL__)) #endif #if defined(__PGIC__) # pragma message(HUNTER_INFO(__PGIC__)) #endif #if defined(__PIC__) # pragma message(HUNTER_INFO(__PIC__)) #endif #if defined(__PIE__) # pragma message(HUNTER_INFO(__PIE__)) #endif #if defined(__POINTER_WIDTH__) # pragma message(HUNTER_INFO(__POINTER_WIDTH__)) #endif #if defined(__POWERPC__) # pragma message(HUNTER_INFO(__POWERPC__)) #endif #if defined(__PPCBROADWAY__) # pragma message(HUNTER_INFO(__PPCBROADWAY__)) #endif #if defined(__PPCGECKO__) # pragma message(HUNTER_INFO(__PPCGECKO__)) #endif #if defined(__PRAGMA_REDEFINE_EXTNAME) # pragma message(HUNTER_INFO(__PRAGMA_REDEFINE_EXTNAME)) #endif #if defined(__PTRDIFF_MAX__) # pragma message(HUNTER_INFO(__PTRDIFF_MAX__)) #endif #if defined(__PTRDIFF_TYPE__) # pragma message(HUNTER_INFO(__PTRDIFF_TYPE__)) #endif #if defined(__PTRDIFF_WIDTH__) # pragma message(HUNTER_INFO(__PTRDIFF_WIDTH__)) #endif #if defined(__QNXNTO__) # pragma message(HUNTER_INFO(__QNXNTO__)) #endif #if defined(__QNX__) # pragma message(HUNTER_INFO(__QNX__)) #endif #if defined(__QQ_FBIT__) # pragma message(HUNTER_INFO(__QQ_FBIT__)) #endif #if defined(__QQ_IBIT__) # pragma message(HUNTER_INFO(__QQ_IBIT__)) #endif #if defined(__REGISTER_PREFIX__) # pragma message(HUNTER_INFO(__REGISTER_PREFIX__)) #endif #if defined(__RISC2_0__) # pragma message(HUNTER_INFO(__RISC2_0__)) #endif #if defined(__SACCUM_EPSILON__) # pragma message(HUNTER_INFO(__SACCUM_EPSILON__)) #endif #if defined(__SACCUM_FBIT__) # pragma message(HUNTER_INFO(__SACCUM_FBIT__)) #endif #if defined(__SACCUM_IBIT__) # pragma message(HUNTER_INFO(__SACCUM_IBIT__)) #endif #if defined(__SACCUM_MAX__) # pragma message(HUNTER_INFO(__SACCUM_MAX__)) #endif #if defined(__SACCUM_MIN__) # pragma message(HUNTER_INFO(__SACCUM_MIN__)) #endif #if defined(__SA_FBIT__) # pragma message(HUNTER_INFO(__SA_FBIT__)) #endif #if defined(__SA_IBIT__) # pragma message(HUNTER_INFO(__SA_IBIT__)) #endif #if defined(__SCHAR_MAX__) # pragma message(HUNTER_INFO(__SCHAR_MAX__)) #endif #if defined(__SEG_FS) # pragma message(HUNTER_INFO(__SEG_FS)) #endif #if defined(__SEG_GS) # pragma message(HUNTER_INFO(__SEG_GS)) #endif #if defined(__SFRACT_EPSILON__) # pragma message(HUNTER_INFO(__SFRACT_EPSILON__)) #endif #if defined(__SFRACT_FBIT__) # pragma message(HUNTER_INFO(__SFRACT_FBIT__)) #endif #if defined(__SFRACT_IBIT__) # pragma message(HUNTER_INFO(__SFRACT_IBIT__)) #endif #if defined(__SFRACT_MAX__) # pragma message(HUNTER_INFO(__SFRACT_MAX__)) #endif #if defined(__SFRACT_MIN__) # pragma message(HUNTER_INFO(__SFRACT_MIN__)) #endif #if defined(__SGI_STL) # pragma message(HUNTER_INFO(__SGI_STL)) #endif #if defined(__SGI_STL_PORT) # pragma message(HUNTER_INFO(__SGI_STL_PORT)) #endif #if defined(__SH3__) # pragma message(HUNTER_INFO(__SH3__)) #endif #if defined(__SH4__) # pragma message(HUNTER_INFO(__SH4__)) #endif #if defined(__SH5__) # pragma message(HUNTER_INFO(__SH5__)) #endif #if defined(__SHRT_MAX__) # pragma message(HUNTER_INFO(__SHRT_MAX__)) #endif #if defined(__SIG_ATOMIC_MAX__) # pragma message(HUNTER_INFO(__SIG_ATOMIC_MAX__)) #endif #if defined(__SIG_ATOMIC_MIN__) # pragma message(HUNTER_INFO(__SIG_ATOMIC_MIN__)) #endif #if defined(__SIG_ATOMIC_TYPE__) # pragma message(HUNTER_INFO(__SIG_ATOMIC_TYPE__)) #endif #if defined(__SIG_ATOMIC_WIDTH__) # pragma message(HUNTER_INFO(__SIG_ATOMIC_WIDTH__)) #endif #if defined(__SIZEOF_DOUBLE__) # pragma message(HUNTER_INFO(__SIZEOF_DOUBLE__)) #endif #if defined(__SIZEOF_FLOAT128__) # pragma message(HUNTER_INFO(__SIZEOF_FLOAT128__)) #endif #if defined(__SIZEOF_FLOAT80__) # pragma message(HUNTER_INFO(__SIZEOF_FLOAT80__)) #endif #if defined(__SIZEOF_FLOAT__) # pragma message(HUNTER_INFO(__SIZEOF_FLOAT__)) #endif #if defined(__SIZEOF_INT128__) # pragma message(HUNTER_INFO(__SIZEOF_INT128__)) #endif #if defined(__SIZEOF_INT__) # pragma message(HUNTER_INFO(__SIZEOF_INT__)) #endif #if defined(__SIZEOF_LONG_DOUBLE__) # pragma message(HUNTER_INFO(__SIZEOF_LONG_DOUBLE__)) #endif #if defined(__SIZEOF_LONG_LONG__) # pragma message(HUNTER_INFO(__SIZEOF_LONG_LONG__)) #endif #if defined(__SIZEOF_LONG__) # pragma message(HUNTER_INFO(__SIZEOF_LONG__)) #endif #if defined(__SIZEOF_POINTER__) # pragma message(HUNTER_INFO(__SIZEOF_POINTER__)) #endif #if defined(__SIZEOF_PTRDIFF_T__) # pragma message(HUNTER_INFO(__SIZEOF_PTRDIFF_T__)) #endif #if defined(__SIZEOF_SHORT__) # pragma message(HUNTER_INFO(__SIZEOF_SHORT__)) #endif #if defined(__SIZEOF_SIZE_T__) # pragma message(HUNTER_INFO(__SIZEOF_SIZE_T__)) #endif #if defined(__SIZEOF_WCHAR_T__) # pragma message(HUNTER_INFO(__SIZEOF_WCHAR_T__)) #endif #if defined(__SIZEOF_WINT_T__) # pragma message(HUNTER_INFO(__SIZEOF_WINT_T__)) #endif #if defined(__SIZE_MAX__) # pragma message(HUNTER_INFO(__SIZE_MAX__)) #endif #if defined(__SIZE_TYPE__) # pragma message(HUNTER_INFO(__SIZE_TYPE__)) #endif #if defined(__SIZE_WIDTH__) # pragma message(HUNTER_INFO(__SIZE_WIDTH__)) #endif #if defined(__SQ_FBIT__) # pragma message(HUNTER_INFO(__SQ_FBIT__)) #endif #if defined(__SQ_IBIT__) # pragma message(HUNTER_INFO(__SQ_IBIT__)) #endif #if defined(__SSE2_MATH__) # pragma message(HUNTER_INFO(__SSE2_MATH__)) #endif #if defined(__SSE2__) # pragma message(HUNTER_INFO(__SSE2__)) #endif #if defined(__SSE3__) # pragma message(HUNTER_INFO(__SSE3__)) #endif #if defined(__SSE4A__) # pragma message(HUNTER_INFO(__SSE4A__)) #endif #if defined(__SSE4_1__) # pragma message(HUNTER_INFO(__SSE4_1__)) #endif #if defined(__SSE4_2__) # pragma message(HUNTER_INFO(__SSE4_2__)) #endif #if defined(__SSE_MATH__) # pragma message(HUNTER_INFO(__SSE_MATH__)) #endif #if defined(__SSE__) # pragma message(HUNTER_INFO(__SSE__)) #endif #if defined(__SSP_STRONG__) # pragma message(HUNTER_INFO(__SSP_STRONG__)) #endif #if defined(__SSP__) # pragma message(HUNTER_INFO(__SSP__)) #endif #if defined(__SSSE3__) # pragma message(HUNTER_INFO(__SSSE3__)) #endif #if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) # pragma message(HUNTER_INFO(__STDCPP_DEFAULT_NEW_ALIGNMENT__)) #endif #if defined(__STDCPP_STRICT_POINTER_SAFETY__) # pragma message(HUNTER_INFO(__STDCPP_STRICT_POINTER_SAFETY__)) #endif #if defined(__STDCPP_THREADS__) # pragma message(HUNTER_INFO(__STDCPP_THREADS__)) #endif #if defined(__STDC_HOSTED__) # pragma message(HUNTER_INFO(__STDC_HOSTED__)) #endif #if defined(__STDC_IEC_559_COMPLEX__) # pragma message(HUNTER_INFO(__STDC_IEC_559_COMPLEX__)) #endif #if defined(__STDC_IEC_559__) # pragma message(HUNTER_INFO(__STDC_IEC_559__)) #endif #if defined(__STDC_ISO_10646__) # pragma message(HUNTER_INFO(__STDC_ISO_10646__)) #endif #if defined(__STDC_MB_MIGHT_NEQ_WC__) # pragma message(HUNTER_INFO(__STDC_MB_MIGHT_NEQ_WC__)) #endif #if defined(__STDC_NO_THREADS__) # pragma message(HUNTER_INFO(__STDC_NO_THREADS__)) #endif #if defined(__STDC_UTF_16__) # pragma message(HUNTER_INFO(__STDC_UTF_16__)) #endif #if defined(__STDC_UTF_32__) # pragma message(HUNTER_INFO(__STDC_UTF_32__)) #endif #if defined(__STDC_VERSION__) # pragma message(HUNTER_INFO(__STDC_VERSION__)) #endif #if defined(__STDC__) # pragma message(HUNTER_INFO(__STDC__)) #endif #if defined(__STD_RWCOMPILER_H__) # pragma message(HUNTER_INFO(__STD_RWCOMPILER_H__)) #endif #if defined(__STL_CONFIG_H) # pragma message(HUNTER_INFO(__STL_CONFIG_H)) #endif #if defined(__SUNPRO_C) # pragma message(HUNTER_INFO(__SUNPRO_C)) #endif #if defined(__SUNPRO_CC) # pragma message(HUNTER_INFO(__SUNPRO_CC)) #endif #if defined(__SVR4) # pragma message(HUNTER_INFO(__SVR4)) #endif #if defined(__SYSC_ZARCH__) # pragma message(HUNTER_INFO(__SYSC_ZARCH__)) #endif #if defined(__SYSC__) # pragma message(HUNTER_INFO(__SYSC__)) #endif #if defined(__TARGET_ARCH_ARM) # pragma message(HUNTER_INFO(__TARGET_ARCH_ARM)) #endif #if defined(__TARGET_ARCH_THUMB) # pragma message(HUNTER_INFO(__TARGET_ARCH_THUMB)) #endif #if defined(__TARGET_LIB__) # pragma message(HUNTER_INFO(__TARGET_LIB__)) #endif #if defined(__TA_FBIT__) # pragma message(HUNTER_INFO(__TA_FBIT__)) #endif #if defined(__TA_IBIT__) # pragma message(HUNTER_INFO(__TA_IBIT__)) #endif #if defined(__THUMBEB__) # pragma message(HUNTER_INFO(__THUMBEB__)) #endif #if defined(__THUMBEL__) # pragma message(HUNTER_INFO(__THUMBEL__)) #endif #if defined(__THUMB_INTERWORK__) # pragma message(HUNTER_INFO(__THUMB_INTERWORK__)) #endif #if defined(__THW_370__) # pragma message(HUNTER_INFO(__THW_370__)) #endif #if defined(__THW_INTEL__) # pragma message(HUNTER_INFO(__THW_INTEL__)) #endif #if defined(__THW_RS6000) # pragma message(HUNTER_INFO(__THW_RS6000)) #endif #if defined(__TOS_AIX__) # pragma message(HUNTER_INFO(__TOS_AIX__)) #endif #if defined(__TOS_WIN__) # pragma message(HUNTER_INFO(__TOS_WIN__)) #endif #if defined(__TQ_FBIT__) # pragma message(HUNTER_INFO(__TQ_FBIT__)) #endif #if defined(__TQ_IBIT__) # pragma message(HUNTER_INFO(__TQ_IBIT__)) #endif #if defined(__TenDRA__) # pragma message(HUNTER_INFO(__TenDRA__)) #endif #if defined(__UACCUM_EPSILON__) # pragma message(HUNTER_INFO(__UACCUM_EPSILON__)) #endif #if defined(__UACCUM_FBIT__) # pragma message(HUNTER_INFO(__UACCUM_FBIT__)) #endif #if defined(__UACCUM_IBIT__) # pragma message(HUNTER_INFO(__UACCUM_IBIT__)) #endif #if defined(__UACCUM_MAX__) # pragma message(HUNTER_INFO(__UACCUM_MAX__)) #endif #if defined(__UACCUM_MIN__) # pragma message(HUNTER_INFO(__UACCUM_MIN__)) #endif #if defined(__UCLIBC__) # pragma message(HUNTER_INFO(__UCLIBC__)) #endif #if defined(__UDA_FBIT__) # pragma message(HUNTER_INFO(__UDA_FBIT__)) #endif #if defined(__UDA_IBIT__) # pragma message(HUNTER_INFO(__UDA_IBIT__)) #endif #if defined(__UDQ_FBIT__) # pragma message(HUNTER_INFO(__UDQ_FBIT__)) #endif #if defined(__UDQ_IBIT__) # pragma message(HUNTER_INFO(__UDQ_IBIT__)) #endif #if defined(__UFRACT_EPSILON__) # pragma message(HUNTER_INFO(__UFRACT_EPSILON__)) #endif #if defined(__UFRACT_FBIT__) # pragma message(HUNTER_INFO(__UFRACT_FBIT__)) #endif #if defined(__UFRACT_IBIT__) # pragma message(HUNTER_INFO(__UFRACT_IBIT__)) #endif #if defined(__UFRACT_MAX__) # pragma message(HUNTER_INFO(__UFRACT_MAX__)) #endif #if defined(__UFRACT_MIN__) # pragma message(HUNTER_INFO(__UFRACT_MIN__)) #endif #if defined(__UHA_FBIT__) # pragma message(HUNTER_INFO(__UHA_FBIT__)) #endif #if defined(__UHA_IBIT__) # pragma message(HUNTER_INFO(__UHA_IBIT__)) #endif #if defined(__UHQ_FBIT__) # pragma message(HUNTER_INFO(__UHQ_FBIT__)) #endif #if defined(__UHQ_IBIT__) # pragma message(HUNTER_INFO(__UHQ_IBIT__)) #endif #if defined(__UINT16_C) # pragma message(HUNTER_INFO(__UINT16_C)) #endif #if defined(__UINT16_MAX__) # pragma message(HUNTER_INFO(__UINT16_MAX__)) #endif #if defined(__UINT16_TYPE__) # pragma message(HUNTER_INFO(__UINT16_TYPE__)) #endif #if defined(__UINT32_C) # pragma message(HUNTER_INFO(__UINT32_C)) #endif #if defined(__UINT32_MAX__) # pragma message(HUNTER_INFO(__UINT32_MAX__)) #endif #if defined(__UINT32_TYPE__) # pragma message(HUNTER_INFO(__UINT32_TYPE__)) #endif #if defined(__UINT64_C) # pragma message(HUNTER_INFO(__UINT64_C)) #endif #if defined(__UINT64_MAX__) # pragma message(HUNTER_INFO(__UINT64_MAX__)) #endif #if defined(__UINT64_TYPE__) # pragma message(HUNTER_INFO(__UINT64_TYPE__)) #endif #if defined(__UINT8_C) # pragma message(HUNTER_INFO(__UINT8_C)) #endif #if defined(__UINT8_MAX__) # pragma message(HUNTER_INFO(__UINT8_MAX__)) #endif #if defined(__UINT8_TYPE__) # pragma message(HUNTER_INFO(__UINT8_TYPE__)) #endif #if defined(__UINTMAX_C) # pragma message(HUNTER_INFO(__UINTMAX_C)) #endif #if defined(__UINTMAX_MAX__) # pragma message(HUNTER_INFO(__UINTMAX_MAX__)) #endif #if defined(__UINTMAX_TYPE__) # pragma message(HUNTER_INFO(__UINTMAX_TYPE__)) #endif #if defined(__UINTPTR_MAX__) # pragma message(HUNTER_INFO(__UINTPTR_MAX__)) #endif #if defined(__UINTPTR_TYPE__) # pragma message(HUNTER_INFO(__UINTPTR_TYPE__)) #endif #if defined(__UINT_FAST16_MAX__) # pragma message(HUNTER_INFO(__UINT_FAST16_MAX__)) #endif #if defined(__UINT_FAST16_TYPE__) # pragma message(HUNTER_INFO(__UINT_FAST16_TYPE__)) #endif #if defined(__UINT_FAST32_MAX__) # pragma message(HUNTER_INFO(__UINT_FAST32_MAX__)) #endif #if defined(__UINT_FAST32_TYPE__) # pragma message(HUNTER_INFO(__UINT_FAST32_TYPE__)) #endif #if defined(__UINT_FAST64_MAX__) # pragma message(HUNTER_INFO(__UINT_FAST64_MAX__)) #endif #if defined(__UINT_FAST64_TYPE__) # pragma message(HUNTER_INFO(__UINT_FAST64_TYPE__)) #endif #if defined(__UINT_FAST8_MAX__) # pragma message(HUNTER_INFO(__UINT_FAST8_MAX__)) #endif #if defined(__UINT_FAST8_TYPE__) # pragma message(HUNTER_INFO(__UINT_FAST8_TYPE__)) #endif #if defined(__UINT_LEAST16_MAX__) # pragma message(HUNTER_INFO(__UINT_LEAST16_MAX__)) #endif #if defined(__UINT_LEAST16_TYPE__) # pragma message(HUNTER_INFO(__UINT_LEAST16_TYPE__)) #endif #if defined(__UINT_LEAST32_MAX__) # pragma message(HUNTER_INFO(__UINT_LEAST32_MAX__)) #endif #if defined(__UINT_LEAST32_TYPE__) # pragma message(HUNTER_INFO(__UINT_LEAST32_TYPE__)) #endif #if defined(__UINT_LEAST64_MAX__) # pragma message(HUNTER_INFO(__UINT_LEAST64_MAX__)) #endif #if defined(__UINT_LEAST64_TYPE__) # pragma message(HUNTER_INFO(__UINT_LEAST64_TYPE__)) #endif #if defined(__UINT_LEAST8_MAX__) # pragma message(HUNTER_INFO(__UINT_LEAST8_MAX__)) #endif #if defined(__UINT_LEAST8_TYPE__) # pragma message(HUNTER_INFO(__UINT_LEAST8_TYPE__)) #endif #if defined(__ULACCUM_EPSILON__) # pragma message(HUNTER_INFO(__ULACCUM_EPSILON__)) #endif #if defined(__ULACCUM_FBIT__) # pragma message(HUNTER_INFO(__ULACCUM_FBIT__)) #endif #if defined(__ULACCUM_IBIT__) # pragma message(HUNTER_INFO(__ULACCUM_IBIT__)) #endif #if defined(__ULACCUM_MAX__) # pragma message(HUNTER_INFO(__ULACCUM_MAX__)) #endif #if defined(__ULACCUM_MIN__) # pragma message(HUNTER_INFO(__ULACCUM_MIN__)) #endif #if defined(__ULFRACT_EPSILON__) # pragma message(HUNTER_INFO(__ULFRACT_EPSILON__)) #endif #if defined(__ULFRACT_FBIT__) # pragma message(HUNTER_INFO(__ULFRACT_FBIT__)) #endif #if defined(__ULFRACT_IBIT__) # pragma message(HUNTER_INFO(__ULFRACT_IBIT__)) #endif #if defined(__ULFRACT_MAX__) # pragma message(HUNTER_INFO(__ULFRACT_MAX__)) #endif #if defined(__ULFRACT_MIN__) # pragma message(HUNTER_INFO(__ULFRACT_MIN__)) #endif #if defined(__ULLACCUM_EPSILON__) # pragma message(HUNTER_INFO(__ULLACCUM_EPSILON__)) #endif #if defined(__ULLACCUM_FBIT__) # pragma message(HUNTER_INFO(__ULLACCUM_FBIT__)) #endif #if defined(__ULLACCUM_IBIT__) # pragma message(HUNTER_INFO(__ULLACCUM_IBIT__)) #endif #if defined(__ULLACCUM_MAX__) # pragma message(HUNTER_INFO(__ULLACCUM_MAX__)) #endif #if defined(__ULLACCUM_MIN__) # pragma message(HUNTER_INFO(__ULLACCUM_MIN__)) #endif #if defined(__ULLFRACT_EPSILON__) # pragma message(HUNTER_INFO(__ULLFRACT_EPSILON__)) #endif #if defined(__ULLFRACT_FBIT__) # pragma message(HUNTER_INFO(__ULLFRACT_FBIT__)) #endif #if defined(__ULLFRACT_IBIT__) # pragma message(HUNTER_INFO(__ULLFRACT_IBIT__)) #endif #if defined(__ULLFRACT_MAX__) # pragma message(HUNTER_INFO(__ULLFRACT_MAX__)) #endif #if defined(__ULLFRACT_MIN__) # pragma message(HUNTER_INFO(__ULLFRACT_MIN__)) #endif #if defined(__UQQ_FBIT__) # pragma message(HUNTER_INFO(__UQQ_FBIT__)) #endif #if defined(__UQQ_IBIT__) # pragma message(HUNTER_INFO(__UQQ_IBIT__)) #endif #if defined(__USACCUM_EPSILON__) # pragma message(HUNTER_INFO(__USACCUM_EPSILON__)) #endif #if defined(__USACCUM_FBIT__) # pragma message(HUNTER_INFO(__USACCUM_FBIT__)) #endif #if defined(__USACCUM_IBIT__) # pragma message(HUNTER_INFO(__USACCUM_IBIT__)) #endif #if defined(__USACCUM_MAX__) # pragma message(HUNTER_INFO(__USACCUM_MAX__)) #endif #if defined(__USACCUM_MIN__) # pragma message(HUNTER_INFO(__USACCUM_MIN__)) #endif #if defined(__USA_FBIT__) # pragma message(HUNTER_INFO(__USA_FBIT__)) #endif #if defined(__USA_IBIT__) # pragma message(HUNTER_INFO(__USA_IBIT__)) #endif #if defined(__USER_LABEL_PREFIX__) # pragma message(HUNTER_INFO(__USER_LABEL_PREFIX__)) #endif #if defined(__USFRACT_EPSILON__) # pragma message(HUNTER_INFO(__USFRACT_EPSILON__)) #endif #if defined(__USFRACT_FBIT__) # pragma message(HUNTER_INFO(__USFRACT_FBIT__)) #endif #if defined(__USFRACT_IBIT__) # pragma message(HUNTER_INFO(__USFRACT_IBIT__)) #endif #if defined(__USFRACT_MAX__) # pragma message(HUNTER_INFO(__USFRACT_MAX__)) #endif #if defined(__USFRACT_MIN__) # pragma message(HUNTER_INFO(__USFRACT_MIN__)) #endif #if defined(__USING_SJLJ_EXCEPTIONS__) # pragma message(HUNTER_INFO(__USING_SJLJ_EXCEPTIONS__)) #endif #if defined(__USQ_FBIT__) # pragma message(HUNTER_INFO(__USQ_FBIT__)) #endif #if defined(__USQ_IBIT__) # pragma message(HUNTER_INFO(__USQ_IBIT__)) #endif #if defined(__UTA_FBIT__) # pragma message(HUNTER_INFO(__UTA_FBIT__)) #endif #if defined(__UTA_IBIT__) # pragma message(HUNTER_INFO(__UTA_IBIT__)) #endif #if defined(__UTQ_FBIT__) # pragma message(HUNTER_INFO(__UTQ_FBIT__)) #endif #if defined(__UTQ_IBIT__) # pragma message(HUNTER_INFO(__UTQ_IBIT__)) #endif #if defined(__VECTOR4DOUBLE__) # pragma message(HUNTER_INFO(__VECTOR4DOUBLE__)) #endif #if defined(__VEC__) # pragma message(HUNTER_INFO(__VEC__)) #endif #if defined(__VERSION__) # pragma message(HUNTER_INFO(__VERSION__)) #endif #if defined(__VFP_FP__) # pragma message(HUNTER_INFO(__VFP_FP__)) #endif #if defined(__VMS) # pragma message(HUNTER_INFO(__VMS)) #endif #if defined(__VMS_VER) # pragma message(HUNTER_INFO(__VMS_VER)) #endif #if defined(__VSX__) # pragma message(HUNTER_INFO(__VSX__)) #endif #if defined(__WATCOMC__) # pragma message(HUNTER_INFO(__WATCOMC__)) #endif #if defined(__WCHAR_MAX__) # pragma message(HUNTER_INFO(__WCHAR_MAX__)) #endif #if defined(__WCHAR_MIN__) # pragma message(HUNTER_INFO(__WCHAR_MIN__)) #endif #if defined(__WCHAR_TYPE__) # pragma message(HUNTER_INFO(__WCHAR_TYPE__)) #endif #if defined(__WCHAR_UNSIGNED__) # pragma message(HUNTER_INFO(__WCHAR_UNSIGNED__)) #endif #if defined(__WCHAR_WIDTH__) # pragma message(HUNTER_INFO(__WCHAR_WIDTH__)) #endif #if defined(__WIN32__) # pragma message(HUNTER_INFO(__WIN32__)) #endif #if defined(__WINDOWS__) # pragma message(HUNTER_INFO(__WINDOWS__)) #endif #if defined(__WINT_MAX__) # pragma message(HUNTER_INFO(__WINT_MAX__)) #endif #if defined(__WINT_MIN__) # pragma message(HUNTER_INFO(__WINT_MIN__)) #endif #if defined(__WINT_TYPE__) # pragma message(HUNTER_INFO(__WINT_TYPE__)) #endif #if defined(__WINT_WIDTH__) # pragma message(HUNTER_INFO(__WINT_WIDTH__)) #endif #if defined(__XOP__) # pragma message(HUNTER_INFO(__XOP__)) #endif #if defined(__aarch64__) # pragma message(HUNTER_INFO(__aarch64__)) #endif #if defined(__alpha) # pragma message(HUNTER_INFO(__alpha)) #endif #if defined(__alpha__) # pragma message(HUNTER_INFO(__alpha__)) #endif #if defined(__alpha_ev4__) # pragma message(HUNTER_INFO(__alpha_ev4__)) #endif #if defined(__alpha_ev5__) # pragma message(HUNTER_INFO(__alpha_ev5__)) #endif #if defined(__alpha_ev6__) # pragma message(HUNTER_INFO(__alpha_ev6__)) #endif #if defined(__amd64) # pragma message(HUNTER_INFO(__amd64)) #endif #if defined(__amd64__) # pragma message(HUNTER_INFO(__amd64__)) #endif #if defined(__amigaos__) # pragma message(HUNTER_INFO(__amigaos__)) #endif #if defined(__apple_build_version__) # pragma message(HUNTER_INFO(__apple_build_version__)) #endif #if defined(__arm) # pragma message(HUNTER_INFO(__arm)) #endif #if defined(__arm64) # pragma message(HUNTER_INFO(__arm64)) #endif #if defined(__arm64__) # pragma message(HUNTER_INFO(__arm64__)) #endif #if defined(__arm__) # pragma message(HUNTER_INFO(__arm__)) #endif #if defined(__bfin__) # pragma message(HUNTER_INFO(__bfin__)) #endif #if defined(__block) # pragma message(HUNTER_INFO(__block)) #endif #if defined(__bsdi__) # pragma message(HUNTER_INFO(__bsdi__)) #endif #if defined(__clang__) # pragma message(HUNTER_INFO(__clang__)) #endif #if defined(__clang_major__) # pragma message(HUNTER_INFO(__clang_major__)) #endif #if defined(__clang_minor__) # pragma message(HUNTER_INFO(__clang_minor__)) #endif #if defined(__clang_patchlevel__) # pragma message(HUNTER_INFO(__clang_patchlevel__)) #endif #if defined(__clang_version__) # pragma message(HUNTER_INFO(__clang_version__)) #endif #if defined(__code_model_small__) # pragma message(HUNTER_INFO(__code_model_small__)) #endif #if defined(__convex__) # pragma message(HUNTER_INFO(__convex__)) #endif #if defined(__convex_c1__) # pragma message(HUNTER_INFO(__convex_c1__)) #endif #if defined(__convex_c2__) # pragma message(HUNTER_INFO(__convex_c2__)) #endif #if defined(__convex_c32__) # pragma message(HUNTER_INFO(__convex_c32__)) #endif #if defined(__convex_c34__) # pragma message(HUNTER_INFO(__convex_c34__)) #endif #if defined(__convex_c38__) # pragma message(HUNTER_INFO(__convex_c38__)) #endif #if defined(__core2) # pragma message(HUNTER_INFO(__core2)) #endif #if defined(__core2__) # pragma message(HUNTER_INFO(__core2__)) #endif #if defined(__cplusplus) # pragma message(HUNTER_INFO(__cplusplus)) #endif #if defined(__cplusplus_cli) # pragma message(HUNTER_INFO(__cplusplus_cli)) #endif #if defined(__cplusplus_winrt) # pragma message(HUNTER_INFO(__cplusplus_winrt)) #endif #if defined(__cpp_aggregate_nsdmi) # pragma message(HUNTER_INFO(__cpp_aggregate_nsdmi)) #endif #if defined(__cpp_alias_templates) # pragma message(HUNTER_INFO(__cpp_alias_templates)) #endif #if defined(__cpp_attributes) # pragma message(HUNTER_INFO(__cpp_attributes)) #endif #if defined(__cpp_binary_literals) # pragma message(HUNTER_INFO(__cpp_binary_literals)) #endif #if defined(__cpp_constexpr) # pragma message(HUNTER_INFO(__cpp_constexpr)) #endif #if defined(__cpp_decltype) # pragma message(HUNTER_INFO(__cpp_decltype)) #endif #if defined(__cpp_decltype_auto) # pragma message(HUNTER_INFO(__cpp_decltype_auto)) #endif #if defined(__cpp_delegating_constructors) # pragma message(HUNTER_INFO(__cpp_delegating_constructors)) #endif #if defined(__cpp_digit_separators) # pragma message(HUNTER_INFO(__cpp_digit_separators)) #endif #if defined(__cpp_exceptions) # pragma message(HUNTER_INFO(__cpp_exceptions)) #endif #if defined(__cpp_generic_lambdas) # pragma message(HUNTER_INFO(__cpp_generic_lambdas)) #endif #if defined(__cpp_hex_float) # pragma message(HUNTER_INFO(__cpp_hex_float)) #endif #if defined(__cpp_inheriting_constructors) # pragma message(HUNTER_INFO(__cpp_inheriting_constructors)) #endif #if defined(__cpp_init_captures) # pragma message(HUNTER_INFO(__cpp_init_captures)) #endif #if defined(__cpp_initializer_lists) # pragma message(HUNTER_INFO(__cpp_initializer_lists)) #endif #if defined(__cpp_lambdas) # pragma message(HUNTER_INFO(__cpp_lambdas)) #endif #if defined(__cpp_nsdmi) # pragma message(HUNTER_INFO(__cpp_nsdmi)) #endif #if defined(__cpp_range_based_for) # pragma message(HUNTER_INFO(__cpp_range_based_for)) #endif #if defined(__cpp_raw_strings) # pragma message(HUNTER_INFO(__cpp_raw_strings)) #endif #if defined(__cpp_ref_qualifiers) # pragma message(HUNTER_INFO(__cpp_ref_qualifiers)) #endif #if defined(__cpp_return_type_deduction) # pragma message(HUNTER_INFO(__cpp_return_type_deduction)) #endif #if defined(__cpp_rtti) # pragma message(HUNTER_INFO(__cpp_rtti)) #endif #if defined(__cpp_runtime_arrays) # pragma message(HUNTER_INFO(__cpp_runtime_arrays)) #endif #if defined(__cpp_rvalue_reference) # pragma message(HUNTER_INFO(__cpp_rvalue_reference)) #endif #if defined(__cpp_sized_deallocation) # pragma message(HUNTER_INFO(__cpp_sized_deallocation)) #endif #if defined(__cpp_static_assert) # pragma message(HUNTER_INFO(__cpp_static_assert)) #endif #if defined(__cpp_unicode_characters) # pragma message(HUNTER_INFO(__cpp_unicode_characters)) #endif #if defined(__cpp_unicode_literals) # pragma message(HUNTER_INFO(__cpp_unicode_literals)) #endif #if defined(__cpp_user_defined_literals) # pragma message(HUNTER_INFO(__cpp_user_defined_literals)) #endif #if defined(__cpp_variable_templates) # pragma message(HUNTER_INFO(__cpp_variable_templates)) #endif #if defined(__cpp_variadic_templates) # pragma message(HUNTER_INFO(__cpp_variadic_templates)) #endif #if defined(__embedded_cplusplus) # pragma message(HUNTER_INFO(__embedded_cplusplus)) #endif #if defined(__ghs) # pragma message(HUNTER_INFO(__ghs)) #endif #if defined(__ghs__) # pragma message(HUNTER_INFO(__ghs__)) #endif #if defined(__gnu_linux__) # pragma message(HUNTER_INFO(__gnu_linux__)) #endif #if defined(__hppa) # pragma message(HUNTER_INFO(__hppa)) #endif #if defined(__hppa__) # pragma message(HUNTER_INFO(__hppa__)) #endif #if defined(__hpux) # pragma message(HUNTER_INFO(__hpux)) #endif #if defined(__i386) # pragma message(HUNTER_INFO(__i386)) #endif #if defined(__i386__) # pragma message(HUNTER_INFO(__i386__)) #endif #if defined(__i486__) # pragma message(HUNTER_INFO(__i486__)) #endif #if defined(__i586__) # pragma message(HUNTER_INFO(__i586__)) #endif #if defined(__i686__) # pragma message(HUNTER_INFO(__i686__)) #endif #if defined(__ia64) # pragma message(HUNTER_INFO(__ia64)) #endif #if defined(__ia64__) # pragma message(HUNTER_INFO(__ia64__)) #endif #if defined(__itanium__) # pragma message(HUNTER_INFO(__itanium__)) #endif #if defined(__k8) # pragma message(HUNTER_INFO(__k8)) #endif #if defined(__k8__) # pragma message(HUNTER_INFO(__k8__)) #endif #if defined(__linux) # pragma message(HUNTER_INFO(__linux)) #endif #if defined(__linux__) # pragma message(HUNTER_INFO(__linux__)) #endif #if defined(__llvm__) # pragma message(HUNTER_INFO(__llvm__)) #endif #if defined(__m68k__) # pragma message(HUNTER_INFO(__m68k__)) #endif #if defined(__mc68000) # pragma message(HUNTER_INFO(__mc68000)) #endif #if defined(__mc68000__) # pragma message(HUNTER_INFO(__mc68000__)) #endif #if defined(__mc68010) # pragma message(HUNTER_INFO(__mc68010)) #endif #if defined(__mc68010__) # pragma message(HUNTER_INFO(__mc68010__)) #endif #if defined(__mc68020) # pragma message(HUNTER_INFO(__mc68020)) #endif #if defined(__mc68020__) # pragma message(HUNTER_INFO(__mc68020__)) #endif #if defined(__mc68030) # pragma message(HUNTER_INFO(__mc68030)) #endif #if defined(__mc68030__) # pragma message(HUNTER_INFO(__mc68030__)) #endif #if defined(__mc68040) # pragma message(HUNTER_INFO(__mc68040)) #endif #if defined(__mc68040__) # pragma message(HUNTER_INFO(__mc68040__)) #endif #if defined(__mc68060) # pragma message(HUNTER_INFO(__mc68060)) #endif #if defined(__mc68060__) # pragma message(HUNTER_INFO(__mc68060__)) #endif #if defined(__mips) # pragma message(HUNTER_INFO(__mips)) #endif #if defined(__mips__) # pragma message(HUNTER_INFO(__mips__)) #endif #if defined(__nocona) # pragma message(HUNTER_INFO(__nocona)) #endif #if defined(__nocona__) # pragma message(HUNTER_INFO(__nocona__)) #endif #if defined(__pic__) # pragma message(HUNTER_INFO(__pic__)) #endif #if defined(__pie__) # pragma message(HUNTER_INFO(__pie__)) #endif #if defined(__powerpc) # pragma message(HUNTER_INFO(__powerpc)) #endif #if defined(__powerpc__) # pragma message(HUNTER_INFO(__powerpc__)) #endif #if defined(__ppc601__) # pragma message(HUNTER_INFO(__ppc601__)) #endif #if defined(__ppc603__) # pragma message(HUNTER_INFO(__ppc603__)) #endif #if defined(__ppc604__) # pragma message(HUNTER_INFO(__ppc604__)) #endif #if defined(__ppc__) # pragma message(HUNTER_INFO(__ppc__)) #endif #if defined(__private_extern__) # pragma message(HUNTER_INFO(__private_extern__)) #endif #if defined(__s390__) # pragma message(HUNTER_INFO(__s390__)) #endif #if defined(__s390x__) # pragma message(HUNTER_INFO(__s390x__)) #endif #if defined(__sgi) # pragma message(HUNTER_INFO(__sgi)) #endif #if defined(__sh1__) # pragma message(HUNTER_INFO(__sh1__)) #endif #if defined(__sh2__) # pragma message(HUNTER_INFO(__sh2__)) #endif #if defined(__sh3__) # pragma message(HUNTER_INFO(__sh3__)) #endif #if defined(__sh__) # pragma message(HUNTER_INFO(__sh__)) #endif #if defined(__sparc) # pragma message(HUNTER_INFO(__sparc)) #endif #if defined(__sparc__) # pragma message(HUNTER_INFO(__sparc__)) #endif #if defined(__sparcv8) # pragma message(HUNTER_INFO(__sparcv8)) #endif #if defined(__sparcv9) # pragma message(HUNTER_INFO(__sparcv9)) #endif #if defined(__strong) # pragma message(HUNTER_INFO(__strong)) #endif #if defined(__sun) # pragma message(HUNTER_INFO(__sun)) #endif #if defined(__svr4__) # pragma message(HUNTER_INFO(__svr4__)) #endif #if defined(__sysv__) # pragma message(HUNTER_INFO(__sysv__)) #endif #if defined(__thumb2__) # pragma message(HUNTER_INFO(__thumb2__)) #endif #if defined(__thumb__) # pragma message(HUNTER_INFO(__thumb__)) #endif #if defined(__tune_core2__) # pragma message(HUNTER_INFO(__tune_core2__)) #endif #if defined(__tune_nocona__) # pragma message(HUNTER_INFO(__tune_nocona__)) #endif #if defined(__unix) # pragma message(HUNTER_INFO(__unix)) #endif #if defined(__unix__) # pragma message(HUNTER_INFO(__unix__)) #endif #if defined(__unsafe_unretained) # pragma message(HUNTER_INFO(__unsafe_unretained)) #endif #if defined(__weak) # pragma message(HUNTER_INFO(__weak)) #endif #if defined(__x86_64) # pragma message(HUNTER_INFO(__x86_64)) #endif #if defined(__x86_64__) # pragma message(HUNTER_INFO(__x86_64__)) #endif #if defined(__xlC__) # pragma message(HUNTER_INFO(__xlC__)) #endif #if defined(__xlc__) # pragma message(HUNTER_INFO(__xlc__)) #endif #if defined(_hpux) # pragma message(HUNTER_INFO(_hpux)) #endif #if defined(__has_feature) # if __has_feature(address_sanitizer) # pragma message(HUNTER_INFO(__HUNTER_DETECT_FEATURE_address_sanitizer)) # endif #endif #if defined(__has_feature) # if __has_feature(memory_sanitizer) # pragma message(HUNTER_INFO(__HUNTER_DETECT_FEATURE_memory_sanitizer)) # endif #endif #if defined(__has_feature) # if __has_feature(thread_sanitizer) # pragma message(HUNTER_INFO(__HUNTER_DETECT_FEATURE_thread_sanitizer)) # endif #endif int main() { } ================================================ FILE: scripts/append-boost-config-macros.cmake.in ================================================ cmake_minimum_required(VERSION 3.0) # run at the boost source root set(boost_user_config_file "boost/config/user.hpp") foreach(s @BOOST_CONFIG_MACROS@) unset(append_str) if("${s}" MATCHES "^([^=]+)=(.+)" ) set(append_str "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") else() set(append_str "#define ${s}") endif() if(append_str) message("-- append '${append_str}' to '${boost_user_config_file}'") file(APPEND "${boost_user_config_file}" "\n${append_str}\n") endif() endforeach() ================================================ FILE: scripts/autotools-merge-lipo.cmake.in ================================================ # Copyright (c) 2015 Ruslan Baratov, Alexandre Pretyman # All rights reserved. ### Input params check string(COMPARE EQUAL "@multi_arch_install_root@" "" is_empty) if(is_empty) message(FATAL_ERROR "multi_arch_install_root is empty") endif() string(COMPARE EQUAL "@ios_architectures@" "" is_empty) if(is_empty) message(FATAL_ERROR "ios_architectures is empty") endif() string(COMPARE EQUAL "@HUNTER_PACKAGE_INSTALL_PREFIX@" "" is_empty) if(is_empty) message(FATAL_ERROR "HUNTER_PACKAGE_INSTALL_PREFIX is empty") endif() set(ios_architectures @ios_architectures@) set(built_arch_roots) foreach(x ${ios_architectures}) list(APPEND built_arch_roots @multi_arch_install_root@/${x}) endforeach() list(LENGTH ios_architectures total_arch_number) # We work with the first root path: we move everything, except the *.a static # library files into @HUNTER_PACKAGE_INSTALL_PREFIX@, then we lipo the static # libraries together into @HUNTER_PACKAGE_INSTALL_PREFIX@ list(GET built_arch_roots 0 first_built_root) file(GLOB_RECURSE binary_files RELATIVE "${first_built_root}" ${first_built_root}/* ) file(GLOB_RECURSE libtool_la_files RELATIVE "${first_built_root}" ${first_built_root}/lib/*.la ) file(GLOB_RECURSE text_files RELATIVE "${first_built_root}" ${first_built_root}/include/* ) # Remove all libtool_la_files from all built roots foreach(arch ${ios_architectures}) foreach(file_name ${libtool_la_files}) file(REMOVE "@multi_arch_install_root@/${arch}/${file_name}" ) endforeach() endforeach() # Exclude text files and .la files from binaries list foreach(x ${libtool_la_files}) list(REMOVE_ITEM binary_files ${x}) endforeach() foreach(x ${text_files}) list(REMOVE_ITEM binary_files ${x}) endforeach() # The preprocessor macros below are from # http://sourceforge.net/p/predef/wiki/Architectures/ # except armv7, which was taken from: # clang -arch armv7 -dD -E config.h function(preprocessor_macro arch result) if(${arch} STREQUAL "armv7") set(${result} "__ARM_ARCH_7A__" PARENT_SCOPE) elseif(${arch} STREQUAL "armv7s") set(${result} "__ARM_ARCH_7S__" PARENT_SCOPE) elseif(${arch} STREQUAL "arm64") set(${result} "__aarch64__" PARENT_SCOPE) elseif(${arch} STREQUAL "i386") set(${result} "__i386__" PARENT_SCOPE) elseif(${arch} STREQUAL "x86_64") set(${result} "__x86_64__" PARENT_SCOPE) else() message(FATAL_ERROR "Architecture: ${arch} is not supported") endif() endfunction() # List the different roots which ${file_name} differ from # ${first_built_root} and store them in result # If result is an empty list, all files are equal set(built_arch_roots_except_first ${built_arch_roots}) list(REMOVE_AT built_arch_roots_except_first 0) function(list_roots_file_diff file_name result) # make different_roots a list, so it can be printed for better investigation set(different_roots) foreach(built_arch_root ${built_arch_roots_except_first}) file(DIFFERENT is_different FILES "${first_built_root}/${file_name}" "${built_arch_root}/${file_name}" ) if(is_different) list(APPEND different_roots "${built_arch_root}/${file_name}") endif() endforeach() set(${result} ${different_roots} PARENT_SCOPE) endfunction() # Function is used when the files of all archs are the same, move # one file to the final destination and delete the others. function(move_one_delete_other file_name) file(RENAME "${first_built_root}/${file_name}" "@HUNTER_PACKAGE_INSTALL_PREFIX@/${file_name}" ) # Remove the unneeded copies foreach(built_arch_root ${built_arch_roots_except_first}) file(REMOVE "${built_arch_root}/${file_name}") endforeach() endfunction() # Merge a header file that differed in content when the project was built with # different architectures. Guard its contents with #ifdef #elif to guarantee # that each architecture gets only its file function(merge_header_diff_archs file_name) set(merged_file_contents " /****************************************************** * File auto generated by Hunter by merging file: * * ${file_name} * which differed its contents in a multi-arch build. * * The supported architectures are: * * ${ios_architectures} *****************************************************/\n" ) set(arch_counter 1) foreach(arch ${ios_architectures}) set(full_path_file_name "@multi_arch_install_root@/${arch}/${file_name}" ) preprocessor_macro(${arch} arch_define) if (arch_counter EQUAL 1) set(merged_file_contents "${merged_file_contents}\n#ifdef ${arch_define}\n" ) else() set(merged_file_contents "${merged_file_contents}\n#elif ${arch_define}\n" ) endif() if(EXISTS "${full_path_file_name}") file(READ ${full_path_file_name} file_contents ) else() set(file_contents "") endif() # Append contents to the merged file set(merged_file_contents "${merged_file_contents}${file_contents}" ) # Discard the file file(REMOVE ${full_path_file_name} ) if (${arch_counter} EQUAL ${total_arch_number}) set(merged_file_contents "${merged_file_contents} #else # error Architecture not supported. It is not one of ${ios_architectures} #endif\n" ) endif() math(EXPR arch_counter ${arch_counter}+1) endforeach() file(WRITE "@HUNTER_PACKAGE_INSTALL_PREFIX@/${file_name}" "${merged_file_contents}" ) endfunction() # Compare for differences between files in built_arch_roots # If files are different and are in the include/ directory # they are merged with #ifdef guards foreach(file_name ${text_files}) get_filename_component(final_dir "@HUNTER_PACKAGE_INSTALL_PREFIX@/${file_name}" DIRECTORY ) file(MAKE_DIRECTORY ${final_dir}) # List the arch roots where the file is different list_roots_file_diff(${file_name} different_roots) list(LENGTH different_roots len) if(${len} EQUAL 0) # Files are the same, so we move them to @HUNTER_PACKAGE_INSTALL_PREFIX@ move_one_delete_other("${file_name}") else() # merge the file contents with #ifdef guards merge_header_diff_archs(${file_name}) endif() endforeach() # we lipo the libraries into @HUNTER_PACKAGE_INSTALL_PREFIX@ foreach(x ${binary_files}) # if the dir to put the library in, does not exist, then create it # this is needed or else lipo could fail get_filename_component(dir "@HUNTER_PACKAGE_INSTALL_PREFIX@/${x}" DIRECTORY) file(MAKE_DIRECTORY "${dir}") set(input_libraries) foreach(built_arch_root ${built_arch_roots}) list(APPEND input_libraries ${built_arch_root}/${x}) endforeach() execute_process( COMMAND lipo -create ${input_libraries} -o "@HUNTER_PACKAGE_INSTALL_PREFIX@/${x}" RESULT_VARIABLE lipo_result ERROR_VARIABLE lipo_error ) if(NOT ${lipo_result} EQUAL 0) message(FATAL_ERROR "lipo execution failed: ${lipo_error}") endif() file(REMOVE ${input_libraries}) endforeach() # Check no files left (i.e. all binaries fused by lipo, all headers merged) file(GLOB_RECURSE files_left "@multi_arch_install_root@/*") string(COMPARE EQUAL "${files_left}" "" is_empty) if(NOT is_empty) message(FATAL_ERROR "Unexpected files: ${files_left}") endif() ================================================ FILE: scripts/clear-all.cmake ================================================ unset(ENV{ACTION}) unset(ENV{AD_HOC_CODE_SIGNING_ALLOWED}) unset(ENV{ALTERNATE_GROUP}) unset(ENV{ALTERNATE_MODE}) unset(ENV{ALTERNATE_OWNER}) unset(ENV{ALWAYS_SEARCH_USER_PATHS}) unset(ENV{ALWAYS_USE_SEPARATE_HEADERMAPS}) unset(ENV{APPLE_INTERNAL_DEVELOPER_DIR}) unset(ENV{APPLE_INTERNAL_DIR}) unset(ENV{APPLE_INTERNAL_DOCUMENTATION_DIR}) unset(ENV{APPLE_INTERNAL_LIBRARY_DIR}) unset(ENV{APPLE_INTERNAL_TOOLS}) unset(ENV{APPLY_RULES_IN_COPY_FILES}) unset(ENV{AR}) unset(ENV{ARCHS}) unset(ENV{ARCHS_STANDARD}) unset(ENV{ARCHS_STANDARD_32_64_BIT}) unset(ENV{ARCHS_STANDARD_32_BIT}) unset(ENV{ARCHS_STANDARD_64_BIT}) unset(ENV{ARCHS_STANDARD_INCLUDING_64_BIT}) unset(ENV{ARCHS_UNIVERSAL_IPHONE_OS}) unset(ENV{AVAILABLE_PLATFORMS}) unset(ENV{BUILD_COMPONENTS}) unset(ENV{BUILD_DIR}) unset(ENV{BUILD_ROOT}) unset(ENV{BUILD_STYLE}) unset(ENV{BUILD_VARIANTS}) unset(ENV{BUILT_PRODUCTS_DIR}) unset(ENV{CACHE_ROOT}) unset(ENV{CCHROOT}) unset(ENV{CC}) unset(ENV{CFLAGS}) unset(ENV{CHMOD}) unset(ENV{CHOWN}) unset(ENV{CLASS_FILE_DIR}) unset(ENV{CLEAN_PRECOMPS}) unset(ENV{CLONE_HEADERS}) unset(ENV{CODESIGNING_FOLDER_PATH}) unset(ENV{CODE_SIGNING_ALLOWED}) unset(ENV{CODE_SIGNING_REQUIRED}) unset(ENV{CODE_SIGN_CONTEXT_CLASS}) unset(ENV{COMBINE_HIDPI_IMAGES}) unset(ENV{COMMAND_MODE}) unset(ENV{COMPOSITE_SDK_DIRS}) unset(ENV{COMPRESS_PNG_FILES}) unset(ENV{CONFIGURATION_BUILD_DIR}) unset(ENV{CONFIGURATION_TEMP_DIR}) unset(ENV{CONFIGURATION}) unset(ENV{CONFIG_SITE}) unset(ENV{COPYING_PRESERVES_HFS_DATA}) unset(ENV{COPY_PHASE_STRIP}) unset(ENV{COPY_RESOURCES_FROM_STATIC_FRAMEWORKS}) unset(ENV{CP}) unset(ENV{CREATE_INFOPLIST_SECTION_IN_BINARY}) unset(ENV{CURRENT_ARCH}) unset(ENV{CURRENT_VARIANT}) unset(ENV{CXX}) unset(ENV{CXXFLAGS}) unset(ENV{DEAD_CODE_STRIPPING}) unset(ENV{DEBUGGING_SYMBOLS}) unset(ENV{DEBUG_INFORMATION_FORMAT}) unset(ENV{DEFAULT_COMPILER}) unset(ENV{DEFAULT_KEXT_INSTALL_PATH}) unset(ENV{DEPLOYMENT_LOCATION}) unset(ENV{DEPLOYMENT_POSTPROCESSING}) unset(ENV{DERIVED_FILES_DIR}) unset(ENV{DERIVED_FILE_DIR}) unset(ENV{DERIVED_SOURCES_DIR}) unset(ENV{DEVELOPER_APPLICATIONS_DIR}) unset(ENV{DEVELOPER_BIN_DIR}) # unset(ENV{DEVELOPER_DIR}) # Used to detect Xcode environment unset(ENV{DEVELOPER_FRAMEWORKS_DIR_QUOTED}) unset(ENV{DEVELOPER_FRAMEWORKS_DIR}) unset(ENV{DEVELOPER_LIBRARY_DIR}) unset(ENV{DEVELOPER_SDK_DIR}) unset(ENV{DEVELOPER_TOOLS_DIR}) unset(ENV{DEVELOPER_USR_DIR}) unset(ENV{DEVELOPMENT_LANGUAGE}) unset(ENV{DO_HEADER_SCANNING_IN_JAM}) unset(ENV{DSTROOT}) unset(ENV{DT_TOOLCHAIN_DIR}) unset(ENV{DWARF_DSYM_FILE_NAME}) unset(ENV{DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT}) unset(ENV{DWARF_DSYM_FOLDER_PATH}) unset(ENV{EFFECTIVE_PLATFORM_NAME}) unset(ENV{EMBEDDED_PROFILE_NAME}) unset(ENV{ENABLE_HEADER_DEPENDENCIES}) unset(ENV{ENTITLEMENTS_REQUIRED}) unset(ENV{EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS}) unset(ENV{EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES}) unset(ENV{FILE_LIST}) unset(ENV{FIXED_FILES_DIR}) unset(ENV{FRAMEWORK_VERSION}) unset(ENV{GCC3_VERSION}) unset(ENV{GCC_GENERATE_DEBUGGING_SYMBOLS}) unset(ENV{GCC_INLINES_ARE_PRIVATE_EXTERN}) unset(ENV{GCC_OPTIMIZATION_LEVEL}) unset(ENV{GCC_PFE_FILE_C_DIALECTS}) unset(ENV{GCC_PREPROCESSOR_DEFINITIONS}) unset(ENV{GCC_SYMBOLS_PRIVATE_EXTERN}) unset(ENV{GCC_THUMB_SUPPORT}) unset(ENV{GCC_TREAT_WARNINGS_AS_ERRORS}) unset(ENV{GCC_VERSION}) unset(ENV{GCC_VERSION_IDENTIFIER}) unset(ENV{GENERATE_MASTER_OBJECT_FILE}) unset(ENV{GENERATE_PKGINFO_FILE}) unset(ENV{GENERATE_PROFILING_CODE}) unset(ENV{GID}) unset(ENV{GROUP}) unset(ENV{HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT}) unset(ENV{HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES}) unset(ENV{HEADERMAP_INCLUDES_NONPUBLIC_NONPRIVATE_HEADERS}) unset(ENV{HEADERMAP_INCLUDES_PROJECT_HEADERS}) unset(ENV{HEADER_SEARCH_PATHS}) unset(ENV{ICONV}) unset(ENV{INFOPLIST_EXPAND_BUILD_SETTINGS}) unset(ENV{INFOPLIST_OUTPUT_FORMAT}) unset(ENV{INFOPLIST_PREPROCESS}) unset(ENV{INSTALL}) unset(ENV{INSTALL_DIR}) unset(ENV{INSTALL_GROUP}) unset(ENV{INSTALL_MODE_FLAG}) unset(ENV{INSTALL_OWNER}) unset(ENV{INSTALL_ROOT}) unset(ENV{IPHONEOS_DEPLOYMENT_TARGET}) # ! unset(ENV{JAVAC_DEFAULT_FLAGS}) unset(ENV{JAVA_APP_STUB}) unset(ENV{JAVA_ARCHIVE_CLASSES}) unset(ENV{JAVA_ARCHIVE_TYPE}) unset(ENV{JAVA_COMPILER}) unset(ENV{JAVA_FRAMEWORK_RESOURCES_DIRS}) unset(ENV{JAVA_JAR_FLAGS}) unset(ENV{JAVA_SOURCE_SUBDIR}) unset(ENV{JAVA_USE_DEPENDENCIES}) unset(ENV{JAVA_ZIP_FLAGS}) unset(ENV{JIKES_DEFAULT_FLAGS}) unset(ENV{KEEP_PRIVATE_EXTERNS}) unset(ENV{LD}) unset(ENV{LDFLAGS}) unset(ENV{LD_DEPENDENCY_INFO_FILE}) unset(ENV{LD_GENERATE_MAP_FILE}) unset(ENV{LD_MAP_FILE_PATH}) unset(ENV{LD_NO_PIE}) unset(ENV{LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVER}) unset(ENV{LEGACY_DEVELOPER_DIR}) unset(ENV{LEX}) unset(ENV{LIBRARY_FLAG_NOSPACE}) unset(ENV{LIBRARY_KEXT_INSTALL_PATH}) unset(ENV{LINKER_DISPLAYS_MANGLED_NAMES}) unset(ENV{LINK_FILE_LIST_normal_armv7}) unset(ENV{LINK_WITH_STANDARD_LIBRARIES}) unset(ENV{LOCAL_ADMIN_APPS_DIR}) unset(ENV{LOCAL_APPS_DIR}) unset(ENV{LOCAL_DEVELOPER_DIR}) unset(ENV{LOCAL_LIBRARY_DIR}) unset(ENV{MAC_OS_X_PRODUCT_BUILD_VERSION}) unset(ENV{MAC_OS_X_VERSION_ACTUAL}) unset(ENV{MAC_OS_X_VERSION_MAJOR}) unset(ENV{MAC_OS_X_VERSION_MINOR}) unset(ENV{MAKEFLAGS}) unset(ENV{MAKELEVEL}) unset(ENV{MFLAGS}) unset(ENV{MODULE_CACHE_DIR}) unset(ENV{NATIVE_ARCH}) unset(ENV{NATIVE_ARCH_32_BIT}) unset(ENV{NATIVE_ARCH_64_BIT}) unset(ENV{NATIVE_ARCH_ACTUAL}) unset(ENV{NO_COMMON}) unset(ENV{OBJDUMP}) unset(ENV{OBJECT_FILE_DIR}) unset(ENV{OBJECT_FILE_DIR_normal}) unset(ENV{OBJROOT}) unset(ENV{ONLY_ACTIVE_ARCH}) unset(ENV{OPTIMIZATION_LEVEL}) unset(ENV{OS}) unset(ENV{OSAC}) unset(ENV{OTHER_CFLAGS}) unset(ENV{OTHER_CPLUSPLUSFLAGS}) unset(ENV{OTHER_LDFLAGS}) unset(ENV{PASCAL_STRINGS}) unset(ENV{PATH_PREFIXES_EXCLUDED_FROM_HEADER_DEPENDENCIES}) unset(ENV{PKGINFO_FILE_PATH}) unset(ENV{PLATFORM_DEVELOPER_APPLICATIONS_DIR}) unset(ENV{PLATFORM_DEVELOPER_BIN_DIR}) unset(ENV{PLATFORM_DEVELOPER_LIBRARY_DIR}) unset(ENV{PLATFORM_DEVELOPER_SDK_DIR}) unset(ENV{PLATFORM_DEVELOPER_TOOLS_DIR}) unset(ENV{PLATFORM_DEVELOPER_USR_DIR}) unset(ENV{PLATFORM_DIR}) unset(ENV{PLATFORM_NAME}) unset(ENV{PLATFORM_PREFERRED_ARCH}) unset(ENV{PLATFORM_PRODUCT_BUILD_VERSION}) unset(ENV{PLIST_FILE_OUTPUT_FORMAT}) unset(ENV{PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR}) unset(ENV{PRECOMP_DESTINATION_DIR}) unset(ENV{PRESERVE_DEAD_CODE_INITS_AND_TERMS}) unset(ENV{PRODUCT_NAME}) unset(ENV{PRODUCT_SETTINGS_PATH}) unset(ENV{PROFILING_CODE}) unset(ENV{PROJECT}) unset(ENV{PROJECT_DERIVED_FILE_DIR}) unset(ENV{PROJECT_DIR}) unset(ENV{PROJECT_FILE_PATH}) unset(ENV{PROJECT_NAME}) unset(ENV{PROJECT_TEMP_DIR}) unset(ENV{PROJECT_TEMP_ROOT}) unset(ENV{RANLIB}) unset(ENV{RC}) unset(ENV{RECURSIVE_SEARCH_PATHS_FOLLOW_SYMLINKS}) unset(ENV{REMOVE_CVS_FROM_RESOURCES}) unset(ENV{REMOVE_GIT_FROM_RESOURCES}) unset(ENV{REMOVE_HG_FROM_RESOURCES}) unset(ENV{REMOVE_SVN_FROM_RESOURCES}) unset(ENV{REZ_COLLECTOR_DIR}) unset(ENV{REZ_OBJECTS_DIR}) unset(ENV{SCAN_ALL_SOURCE_FILES_FOR_INCLUDES}) unset(ENV{SCRIPT_INPUT_FILE_COUNT}) unset(ENV{SCRIPT_OUTPUT_FILE_COUNT}) unset(ENV{SDKROOT}) # ! unset(ENV{SDK_DIR}) unset(ENV{SDK_NAME}) unset(ENV{SDK_PRODUCT_BUILD_VERSION}) unset(ENV{SED}) unset(ENV{SEPARATE_STRIP}) unset(ENV{SEPARATE_SYMBOL_EDIT}) unset(ENV{SET_DIR_MODE_OWNER_GROUP}) unset(ENV{SET_FILE_MODE_OWNER_GROUP}) unset(ENV{SHARED_DERIVED_FILE_DIR}) unset(ENV{SHARED_PRECOMPS_DIR}) unset(ENV{SKIP_INSTALL}) unset(ENV{SOURCE_ROOT}) unset(ENV{SRCROOT}) unset(ENV{STRINGS_FILE_OUTPUT_ENCODING}) unset(ENV{STRIP}) unset(ENV{STRIP_INSTALLED_PRODUCT}) unset(ENV{STRIP_STYLE}) unset(ENV{SUPPORTED_DEVICE_FAMILIES}) unset(ENV{SUPPORTED_PLATFORMS}) unset(ENV{SYMROOT}) unset(ENV{SYSTEM_ADMIN_APPS_DIR}) unset(ENV{SYSTEM_APPS_DIR}) unset(ENV{SYSTEM_CORE_SERVICES_DIR}) unset(ENV{SYSTEM_DEMOS_DIR}) unset(ENV{SYSTEM_DEVELOPER_APPS_DIR}) unset(ENV{SYSTEM_DEVELOPER_BIN_DIR}) unset(ENV{SYSTEM_DEVELOPER_DEMOS_DIR}) unset(ENV{SYSTEM_DEVELOPER_DIR}) unset(ENV{SYSTEM_DEVELOPER_DOC_DIR}) unset(ENV{SYSTEM_DEVELOPER_GRAPHICS_TOOLS_DIR}) unset(ENV{SYSTEM_DEVELOPER_JAVA_TOOLS_DIR}) unset(ENV{SYSTEM_DEVELOPER_PERFORMANCE_TOOLS_DIR}) unset(ENV{SYSTEM_DEVELOPER_RELEASENOTES_DIR}) unset(ENV{SYSTEM_DEVELOPER_TOOLS}) unset(ENV{SYSTEM_DEVELOPER_TOOLS_DOC_DIR}) unset(ENV{SYSTEM_DEVELOPER_TOOLS_RELEASENOTES_DIR}) unset(ENV{SYSTEM_DEVELOPER_USR_DIR}) unset(ENV{SYSTEM_DEVELOPER_UTILITIES_DIR}) unset(ENV{SYSTEM_DOCUMENTATION_DIR}) unset(ENV{SYSTEM_KEXT_INSTALL_PATH}) unset(ENV{SYSTEM_LIBRARY_DIR}) unset(ENV{TARGETED_DEVICE_FAMILY}) unset(ENV{TARGETNAME}) unset(ENV{TARGET_BUILD_DIR}) unset(ENV{TARGET_NAME}) unset(ENV{TARGET_TEMP_DIR}) unset(ENV{TEMP_DIR}) unset(ENV{TEMP_FILES_DIR}) unset(ENV{TEMP_FILE_DIR}) unset(ENV{TEMP_ROOT}) unset(ENV{TEST}) # Break Makefile OpenSSL build unset(ENV{TOOLCHAINS}) unset(ENV{UNSTRIPPED_PRODUCT}) unset(ENV{USER_APPS_DIR}) unset(ENV{USER_LIBRARY_DIR}) unset(ENV{USE_DYNAMIC_NO_PIC}) unset(ENV{USE_HEADERMAP}) unset(ENV{USE_HEADER_SYMLINKS}) unset(ENV{VALIDATE_PRODUCT}) unset(ENV{VALID_ARCHS}) unset(ENV{VERBOSE_PBXCP}) unset(ENV{VERSION_INFO_BUILDER}) unset(ENV{VERSION_INFO_FILE}) unset(ENV{VERSION_INFO_STRING}) unset(ENV{WARNING_CFLAGS}) unset(ENV{XCODE_APP_SUPPORT_DIR}) unset(ENV{XCODE_PRODUCT_BUILD_VERSION}) unset(ENV{XCODE_VERSION_ACTUAL}) unset(ENV{XCODE_VERSION_MAJOR}) unset(ENV{XCODE_VERSION_MINOR}) unset(ENV{XPCSERVICES_FOLDER_PATH}) unset(ENV{YACC}) unset(ENV{arch}) unset(ENV{variant}) if(CMAKE_HOST_UNIX) # Problems with Xcode and boost set(ENV{PATH} "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin") endif() # See bug https://github.com/headupinclouds/gatherer/issues/58 unset(ENV{ANDROID_SDK_ROOT}) unset(ENV{ANDROID_API_VERSION}) unset(ENV{ANDROID_HOME}) # https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android unset(ENV{ANDROID_NDK}) unset(ENV{ANDROID_NDK_ROOT}) unset(ENV{ANDROID_STANDALONE_TOOLCHAIN}) # See clear-all.sh for more info { unset(ENV{PKG_CONFIG_PATH}) unset(ENV{PKG_CONFIG_DEBUG_SPEW}) unset(ENV{PKG_CONFIG_TOP_BUILD_DIR}) unset(ENV{PKG_CONFIG_DISABLE_UNINSTALLED}) unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) unset(ENV{PKG_CONFIG_SYSROOT_DIR}) set(ENV{PKG_CONFIG_LIBDIR} "") # } ================================================ FILE: scripts/clear-all.sh ================================================ unset ACTION unset AD_HOC_CODE_SIGNING_ALLOWED unset ALTERNATE_GROUP unset ALTERNATE_MODE unset ALTERNATE_OWNER unset ALWAYS_SEARCH_USER_PATHS unset ALWAYS_USE_SEPARATE_HEADERMAPS unset APPLE_INTERNAL_DEVELOPER_DIR unset APPLE_INTERNAL_DIR unset APPLE_INTERNAL_DOCUMENTATION_DIR unset APPLE_INTERNAL_LIBRARY_DIR unset APPLE_INTERNAL_TOOLS unset APPLY_RULES_IN_COPY_FILES unset AR unset ARCHS unset ARCHS_STANDARD unset ARCHS_STANDARD_32_64_BIT unset ARCHS_STANDARD_32_BIT unset ARCHS_STANDARD_64_BIT unset ARCHS_STANDARD_INCLUDING_64_BIT unset ARCHS_UNIVERSAL_IPHONE_OS unset AVAILABLE_PLATFORMS unset BUILD_COMPONENTS unset BUILD_DIR unset BUILD_ROOT unset BUILD_STYLE unset BUILD_VARIANTS unset BUILT_PRODUCTS_DIR unset CACHE_ROOT unset CC unset CCHROOT unset CFLAGS unset CHMOD unset CHOWN unset CLASS_FILE_DIR unset CLEAN_PRECOMPS unset CLONE_HEADERS unset CODESIGNING_FOLDER_PATH unset CODE_SIGNING_ALLOWED unset CODE_SIGNING_REQUIRED unset CODE_SIGN_CONTEXT_CLASS unset COMBINE_HIDPI_IMAGES unset COMMAND_MODE unset COMPOSITE_SDK_DIRS unset COMPRESS_PNG_FILES unset CONFIGURATION unset CONFIGURATION_BUILD_DIR unset CONFIGURATION_TEMP_DIR unset CONFIG_SITE unset COPYING_PRESERVES_HFS_DATA unset COPY_PHASE_STRIP unset COPY_RESOURCES_FROM_STATIC_FRAMEWORKS unset CP unset CREATE_INFOPLIST_SECTION_IN_BINARY unset CURRENT_ARCH unset CURRENT_VARIANT unset CXX unset CXXFLAGS unset DEAD_CODE_STRIPPING unset DEBUGGING_SYMBOLS unset DEBUG_INFORMATION_FORMAT unset DEFAULT_COMPILER unset DEFAULT_KEXT_INSTALL_PATH unset DEPLOYMENT_LOCATION unset DEPLOYMENT_POSTPROCESSING unset DERIVED_FILES_DIR unset DERIVED_FILE_DIR unset DERIVED_SOURCES_DIR unset DEVELOPER_APPLICATIONS_DIR unset DEVELOPER_BIN_DIR # unset DEVELOPER_DIR # Used to detect Xcode environment unset DEVELOPER_FRAMEWORKS_DIR unset DEVELOPER_FRAMEWORKS_DIR_QUOTED unset DEVELOPER_LIBRARY_DIR unset DEVELOPER_SDK_DIR unset DEVELOPER_TOOLS_DIR unset DEVELOPER_USR_DIR unset DEVELOPMENT_LANGUAGE unset DO_HEADER_SCANNING_IN_JAM unset DSTROOT unset DT_TOOLCHAIN_DIR unset DWARF_DSYM_FILE_NAME unset DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT unset DWARF_DSYM_FOLDER_PATH unset EFFECTIVE_PLATFORM_NAME unset EMBEDDED_PROFILE_NAME unset ENABLE_HEADER_DEPENDENCIES unset ENTITLEMENTS_REQUIRED unset EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS unset EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES unset FILE_LIST unset FIXED_FILES_DIR unset FRAMEWORK_VERSION unset GCC3_VERSION unset GCC_GENERATE_DEBUGGING_SYMBOLS unset GCC_INLINES_ARE_PRIVATE_EXTERN unset GCC_OPTIMIZATION_LEVEL unset GCC_PFE_FILE_C_DIALECTS unset GCC_PREPROCESSOR_DEFINITIONS unset GCC_SYMBOLS_PRIVATE_EXTERN unset GCC_THUMB_SUPPORT unset GCC_TREAT_WARNINGS_AS_ERRORS unset GCC_VERSION unset GCC_VERSION_IDENTIFIER unset GENERATE_MASTER_OBJECT_FILE unset GENERATE_PKGINFO_FILE unset GENERATE_PROFILING_CODE unset GID unset GROUP unset HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT unset HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES unset HEADERMAP_INCLUDES_NONPUBLIC_NONPRIVATE_HEADERS unset HEADERMAP_INCLUDES_PROJECT_HEADERS unset HEADER_SEARCH_PATHS unset ICONV unset INFOPLIST_EXPAND_BUILD_SETTINGS unset INFOPLIST_OUTPUT_FORMAT unset INFOPLIST_PREPROCESS unset INSTALL unset INSTALL_DIR unset INSTALL_GROUP unset INSTALL_MODE_FLAG unset INSTALL_OWNER unset INSTALL_ROOT unset IPHONEOS_DEPLOYMENT_TARGET # ! unset JAVAC_DEFAULT_FLAGS unset JAVA_APP_STUB unset JAVA_ARCHIVE_CLASSES unset JAVA_ARCHIVE_TYPE unset JAVA_COMPILER unset JAVA_FRAMEWORK_RESOURCES_DIRS unset JAVA_JAR_FLAGS unset JAVA_SOURCE_SUBDIR unset JAVA_USE_DEPENDENCIES unset JAVA_ZIP_FLAGS unset JIKES_DEFAULT_FLAGS unset KEEP_PRIVATE_EXTERNS unset LD unset LDFLAGS unset LD_DEPENDENCY_INFO_FILE unset LD_GENERATE_MAP_FILE unset LD_MAP_FILE_PATH unset LD_NO_PIE unset LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVER unset LEGACY_DEVELOPER_DIR unset LEX unset LIBRARY_FLAG_NOSPACE unset LIBRARY_KEXT_INSTALL_PATH unset LINKER_DISPLAYS_MANGLED_NAMES unset LINK_FILE_LIST_normal_armv7 unset LINK_WITH_STANDARD_LIBRARIES unset LOCAL_ADMIN_APPS_DIR unset LOCAL_APPS_DIR unset LOCAL_DEVELOPER_DIR unset LOCAL_LIBRARY_DIR unset MAC_OS_X_PRODUCT_BUILD_VERSION unset MAC_OS_X_VERSION_ACTUAL unset MAC_OS_X_VERSION_MAJOR unset MAC_OS_X_VERSION_MINOR unset MAKEFLAGS unset MAKELEVEL unset MFLAGS unset MODULE_CACHE_DIR unset NATIVE_ARCH unset NATIVE_ARCH_32_BIT unset NATIVE_ARCH_64_BIT unset NATIVE_ARCH_ACTUAL unset NO_COMMON unset OBJDUMP unset OBJECT_FILE_DIR unset OBJECT_FILE_DIR_normal unset OBJROOT unset ONLY_ACTIVE_ARCH unset OPTIMIZATION_LEVEL unset OS unset OSAC unset OTHER_CFLAGS unset OTHER_CPLUSPLUSFLAGS unset OTHER_LDFLAGS unset PASCAL_STRINGS unset PATH_PREFIXES_EXCLUDED_FROM_HEADER_DEPENDENCIES unset PKGINFO_FILE_PATH unset PLATFORM_DEVELOPER_APPLICATIONS_DIR unset PLATFORM_DEVELOPER_BIN_DIR unset PLATFORM_DEVELOPER_LIBRARY_DIR unset PLATFORM_DEVELOPER_SDK_DIR unset PLATFORM_DEVELOPER_TOOLS_DIR unset PLATFORM_DEVELOPER_USR_DIR unset PLATFORM_DIR unset PLATFORM_NAME unset PLATFORM_PREFERRED_ARCH unset PLATFORM_PRODUCT_BUILD_VERSION unset PLIST_FILE_OUTPUT_FORMAT unset PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR unset PRECOMP_DESTINATION_DIR unset PRESERVE_DEAD_CODE_INITS_AND_TERMS unset PRODUCT_NAME unset PRODUCT_SETTINGS_PATH unset PROFILING_CODE unset PROJECT unset PROJECT_DERIVED_FILE_DIR unset PROJECT_DIR unset PROJECT_FILE_PATH unset PROJECT_NAME unset PROJECT_TEMP_DIR unset PROJECT_TEMP_ROOT unset RANLIB unset RECURSIVE_SEARCH_PATHS_FOLLOW_SYMLINKS unset REMOVE_CVS_FROM_RESOURCES unset REMOVE_GIT_FROM_RESOURCES unset REMOVE_HG_FROM_RESOURCES unset REMOVE_SVN_FROM_RESOURCES unset REZ_COLLECTOR_DIR unset REZ_OBJECTS_DIR unset SCAN_ALL_SOURCE_FILES_FOR_INCLUDES unset SCRIPT_INPUT_FILE_COUNT unset SCRIPT_OUTPUT_FILE_COUNT unset SDKROOT # ! unset SDK_DIR unset SDK_NAME unset SDK_PRODUCT_BUILD_VERSION unset SED unset SEPARATE_STRIP unset SEPARATE_SYMBOL_EDIT unset SET_DIR_MODE_OWNER_GROUP unset SET_FILE_MODE_OWNER_GROUP unset SHARED_DERIVED_FILE_DIR unset SHARED_PRECOMPS_DIR unset SKIP_INSTALL unset SOURCE_ROOT unset SRCROOT unset STRINGS_FILE_OUTPUT_ENCODING unset STRIP unset STRIP_INSTALLED_PRODUCT unset STRIP_STYLE unset SUPPORTED_DEVICE_FAMILIES unset SUPPORTED_PLATFORMS unset SYMROOT unset SYSTEM_ADMIN_APPS_DIR unset SYSTEM_APPS_DIR unset SYSTEM_CORE_SERVICES_DIR unset SYSTEM_DEMOS_DIR unset SYSTEM_DEVELOPER_APPS_DIR unset SYSTEM_DEVELOPER_BIN_DIR unset SYSTEM_DEVELOPER_DEMOS_DIR unset SYSTEM_DEVELOPER_DIR unset SYSTEM_DEVELOPER_DOC_DIR unset SYSTEM_DEVELOPER_GRAPHICS_TOOLS_DIR unset SYSTEM_DEVELOPER_JAVA_TOOLS_DIR unset SYSTEM_DEVELOPER_PERFORMANCE_TOOLS_DIR unset SYSTEM_DEVELOPER_RELEASENOTES_DIR unset SYSTEM_DEVELOPER_TOOLS unset SYSTEM_DEVELOPER_TOOLS_DOC_DIR unset SYSTEM_DEVELOPER_TOOLS_RELEASENOTES_DIR unset SYSTEM_DEVELOPER_USR_DIR unset SYSTEM_DEVELOPER_UTILITIES_DIR unset SYSTEM_DOCUMENTATION_DIR unset SYSTEM_KEXT_INSTALL_PATH unset SYSTEM_LIBRARY_DIR unset TARGETED_DEVICE_FAMILY unset TARGETNAME unset TARGET_BUILD_DIR unset TARGET_NAME unset TARGET_TEMP_DIR unset TEMP_DIR unset TEMP_FILES_DIR unset TEMP_FILE_DIR unset TEMP_ROOT unset TEST # Break Makefile OpenSSL build unset TOOLCHAINS unset UNSTRIPPED_PRODUCT unset USER_APPS_DIR unset USER_LIBRARY_DIR unset USE_DYNAMIC_NO_PIC unset USE_HEADERMAP unset USE_HEADER_SYMLINKS unset VALIDATE_PRODUCT unset VALID_ARCHS unset VERBOSE_PBXCP unset VERSION_INFO_BUILDER unset VERSION_INFO_FILE unset VERSION_INFO_STRING unset WARNING_CFLAGS unset XCODE_APP_SUPPORT_DIR unset XCODE_PRODUCT_BUILD_VERSION unset XCODE_VERSION_ACTUAL unset XCODE_VERSION_MAJOR unset XCODE_VERSION_MINOR unset XPCSERVICES_FOLDER_PATH unset YACC unset arch unset variant # Problems with Xcode and boost export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin # Unset environment variables used by pkg-config { # * http://linux.die.net/man/1/pkg-config unset PKG_CONFIG_PATH unset PKG_CONFIG_DEBUG_SPEW unset PKG_CONFIG_TOP_BUILD_DIR unset PKG_CONFIG_DISABLE_UNINSTALLED unset PKG_CONFIG_ALLOW_SYSTEM_CFLAGS unset PKG_CONFIG_ALLOW_SYSTEM_LIBS unset PKG_CONFIG_SYSROOT_DIR # https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android unset ANDROID_NDK unset ANDROID_NDK_ROOT unset ANDROID_STANDALONE_TOOLCHAIN # This variable should be set to empty. # Example from Ubuntu: # > (unset PKG_CONFIG_LIBDIR && pkg-config --cflags xorg-wacom) # return default path! # > (export PKG_CONFIG_LIBDIR="" && pkg-config --cflags xorg-wacom) # nothing found export PKG_CONFIG_LIBDIR="" # } ================================================ FILE: scripts/copy-files.cmake ================================================ # Copyright (c) 2015 Ruslan Baratov # All rights reserved. string(COMPARE EQUAL "${from}" "" is_empty) if(is_empty) message(FATAL_ERROR "'from' should not be empty") endif() string(COMPARE EQUAL "${to}" "" is_empty) if(is_empty) message(FATAL_ERROR "'to' should not be empty") endif() if(NOT EXISTS "${from}") message(FATAL_ERROR "Directory not exists: ${from}") endif() if(NOT IS_DIRECTORY "${from}") message(FATAL_ERROR "Is not directory: ${from}") endif() file(GLOB files "${from}/*") file(COPY ${files} DESTINATION "${to}") ================================================ FILE: scripts/create-predefined-list.py ================================================ #!/usr/bin/env python3 # Copyright (c) 2014, Ruslan Baratov # All rights reserved. import argparse import os import re import subprocess import urllib.request argparser = argparse.ArgumentParser( description=""" Create C++ source file `ShowPredefined.cpp` which will be used to verify toolchain compatibility. """ ) argparser.add_argument( '--raw', help=""" File with raw list of macroses (used simultaneously as initial list and file to append result list) """ ) argparser.add_argument( '--site', help=""" Read macroses _... from site. Example: http://msdn.microsoft.com/en-us/library/b0084kay.aspx """ ) argparser.add_argument( '--compiler', help=""" GCC-like unix compiler that accept syntax: '-E -x c++ -dM /dev/null' """ ) argparser.add_argument( '--arch', help=""" Architecture for compiler (e.g. armv7, armv7s, arm64, i386, x86_64, ...) """ ) argparser.add_argument( '--boost-predef', action='store_true', help=""" Read macroses from repository: https://github.com/boostorg/predef """ ) args = argparser.parse_args() # This macroses is not quite helpful exclude_list = [ '__DATE__', '__FILE__', '__LINE__', '__TIME__', '__TIMESTAMP__', '__NO_INLINE__', '_DEBUG', '__FUNCTION__', # gcc & clang preprocessor extensions # see https://github.com/ruslo/hunter/pull/425 '__has_include', '__has_include_next', ] macros_list = [] if args.raw: data = open(args.raw, "r").read() macros_list += data.split() if args.site: content = urllib.request.urlopen(args.site).read().decode('utf-8') parsed = re.findall('_[^<]*', content) for x in parsed: macros_list.append(re.sub(r'(.*)', r'\1', x)) if args.boost_predef: temp_dir = os.path.join(os.getcwd(), '__temp-git-predef') if not os.path.exists(temp_dir): subprocess.check_call( ['git', 'clone', 'https://github.com/boostorg/predef', temp_dir] ) gitdir = os.path.join(temp_dir, '.git') docdir = os.path.join(temp_dir, 'doc') assert(os.path.exists(gitdir)) assert(os.path.exists(docdir)) macro = re.compile(r'\bdefined\(_[^)]*\)') boost_macro_list = [] for root, dirs, files in os.walk(temp_dir): if root.startswith(gitdir): continue if root.startswith(docdir): continue for x in files: filename = os.path.join(root, x) boost_macro_list += macro.findall(open(filename).read()) boost_macro_list = list(set(boost_macro_list)) macros_list += [ re.sub(r'^defined\((.*)\)$', r'\1', x) for x in boost_macro_list ] if args.compiler: run_args = [args.compiler, '-E', '-x', 'c++', '-dM', '/dev/null'] if args.arch: run_args += ['-arch', args.arch] macroses = subprocess.check_output(run_args, universal_newlines=True) compiler_macro_list = macroses.split('\n') for x in compiler_macro_list: if re.match(r'^#define _', x): macros_list.append(re.sub(r'^#define ([^ (]*).*', r'\1', x)) macros_list = sorted(set(macros_list)) for to_exclude in exclude_list: if to_exclude in macros_list: macros_list.remove(to_exclude) for x in macros_list: print("> {}".format(x)) if args.raw: result_fl = open(args.raw, 'w') for macro in macros_list: result_fl.write('{}\n'.format(macro)) # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx cpp_head = """ // This file generated automatically by `create-predefined-list.py` script. // * https://github.com/ruslo/hunter #define HUNTER_QUOTE(x) #x #define HUNTER_STRING(x) HUNTER_QUOTE(x) #define HUNTER_INFO(x) \\ "__HUNTER_MACRO_CHECK_BEGIN__" \\ "#define " #x " " HUNTER_STRING(x) \\ "__HUNTER_MACRO_CHECK_END__" #include // Check std library version #if defined(__ANDROID__) # include // Header with __ANDROID_API__ #endif #if defined(_MSC_VER) # include // Header with _WIN32_WINNT #endif """ cpp_one_check = """ #if defined({}) # pragma message(HUNTER_INFO({})) #endif """ # http://clang.llvm.org/docs/AddressSanitizer.html#conditional-compilation-with-has-feature-address-sanitizer sanitize_detect_check = """ #if defined(__has_feature) # if __has_feature({}_sanitizer) # pragma message(HUNTER_INFO(__HUNTER_DETECT_FEATURE_{}_sanitizer)) # endif #endif """ sanitizers_list = [ 'address', # 'leak', # Not detected! 'memory', 'thread' ] cpp_end = """ int main() { } """ if macros_list: cpp_result = open('ShowPredefined.cpp', 'w') cpp_result.write(cpp_head) for x in macros_list: cpp_result.write(cpp_one_check.format(x, x)) for x in sanitizers_list: cpp_result.write(sanitize_detect_check.format(x, x)) cpp_result.write(cpp_end) ================================================ FILE: scripts/create-toolchain-info.cmake ================================================ cmake_minimum_required(VERSION 3.0) project(HunterToolchain) if(NOT HUNTER_SELF) # Emulate 'hunter_internal_error' message("[hunter ** INTERNAL **] HUNTER_SELF is empty") message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") message("") message("------------------------------ WIKI -------------------------------") message(" https://github.com/ruslo/hunter/wiki/error.internal") message("-------------------------------------------------------------------") message(FATAL_ERROR "") endif() list(APPEND CMAKE_MODULE_PATH "${HUNTER_SELF}/cmake/modules") include(hunter_fatal_error) include(hunter_internal_error) if(NOT TOOLCHAIN_INFO_FILE) hunter_internal_error("TOOLCHAIN_INFO_FILE is empty") endif() if(NOT CMAKE_BINARY_DIR) hunter_internal_error("CMAKE_BINARY_DIR empty") endif() if(EXISTS "${TOOLCHAIN_INFO_FILE}") hunter_internal_error("${TOOLCHAIN_INFO_FILE} already exists") endif() include(hunter_assert_not_empty_string) hunter_assert_not_empty_string("${HUNTER_CONFIGURATION_TYPES}") file( WRITE "${TOOLCHAIN_INFO_FILE}" "Cache version: 6\n" "Polly toolchains:\n" " IPHONEOS_ARCHS: ${IPHONEOS_ARCHS}\n" " IPHONESIMULATOR_ARCHS: ${IPHONESIMULATOR_ARCHS}\n" "Other:\n" " CMAKE_GENERATOR: ${CMAKE_GENERATOR}\n" " HUNTER_CONFIGURATION_TYPES: ${HUNTER_CONFIGURATION_TYPES}\n" " HUNTER_TOOLCHAIN_UNDETECTABLE_ID: ${HUNTER_TOOLCHAIN_UNDETECTABLE_ID}\n" ) string(COMPARE EQUAL "${HUNTER_BUILD_SHARED_LIBS}" "" is_empty) if(NOT is_empty) file( APPEND "${TOOLCHAIN_INFO_FILE}" " HUNTER_BUILD_SHARED_LIBS: ${HUNTER_BUILD_SHARED_LIBS}\n" ) endif() string(COMPARE EQUAL "${OSX_SDK_VERSION}" "" is_empty) if(NOT is_empty) file( APPEND "${TOOLCHAIN_INFO_FILE}" " OSX_SDK_VERSION: ${OSX_SDK_VERSION}\n" ) endif() foreach(configuration ${HUNTER_CONFIGURATION_TYPES}) string(TOUPPER "${configuration}" configuration_upper) file(APPEND "${TOOLCHAIN_INFO_FILE}" " CMAKE_${configuration_upper}_POSTFIX: ") file(APPEND "${TOOLCHAIN_INFO_FILE}" "${CMAKE_${configuration_upper}_POSTFIX}\n") endforeach() set(predefined "${HUNTER_SELF}/scripts/ShowPredefined.cpp") if(NOT EXISTS "${predefined}") hunter_internal_error("${predefined} not exists") endif() try_compile( try_compile_result "${CMAKE_BINARY_DIR}/_test" "${predefined}" CMAKE_FLAGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" OUTPUT_VARIABLE outresult ) if(NOT try_compile_result) hunter_internal_error( "Compilation of ${predefined} failed. Result: ${try_compile_result}\n" "Output:\n--- OUTPUT BEGIN ---\n${outresult}\n--- OUTPUT END ---" ) endif() function(split_string string_in result) set("${result}" "") while(TRUE) string(COMPARE EQUAL "${string_in}" "" is_empty) if(is_empty) break() endif() string(FIND "${string_in}" "\n" eol_pos) if(eol_pos EQUAL -1) list(APPEND "${result}" "${string_in}") break() endif() string(SUBSTRING "${string_in}" 0 ${eol_pos} substring) list(APPEND "${result}" "${substring}") math(EXPR eol_pos "${eol_pos} + 1") # Skip EOL character string(SUBSTRING "${string_in}" ${eol_pos} -1 string_in) endwhile() set(${result} "${${result}}" PARENT_SCOPE) endfunction() split_string("${outresult}" list_of_strings) set(macroses "") foreach(x ${list_of_strings}) string( REGEX MATCH "__HUNTER_MACRO_CHECK_BEGIN__.*__HUNTER_MACRO_CHECK_END__" result_x "${x}" ) if(result_x) string( REGEX REPLACE ".*__HUNTER_MACRO_CHECK_BEGIN__\(.*\)__HUNTER_MACRO_CHECK_END__.*" "\\1" result_x "${x}" ) set(macroses "${macroses}${result_x}\n") endif() endforeach() string(COMPARE EQUAL "${macroses}" "" is_empty) if(is_empty) hunter_fatal_error("No toolchain info generated" WIKI error.no.toolchain.info) endif() file(APPEND "${TOOLCHAIN_INFO_FILE}" "Predefined macroses:\n${macroses}") ================================================ FILE: scripts/find_python.cmake ================================================ cmake_minimum_required(VERSION 3.0) find_package(PythonInterp 3 QUIET) if(NOT PYTHONINTERP_FOUND) message(FATAL_ERROR "Python not found") endif() message(${PYTHON_EXECUTABLE}) ================================================ FILE: scripts/link-all.cmake ================================================ cmake_minimum_required(VERSION 3.0) string(COMPARE EQUAL "${HUNTER_INSTALL_PREFIX}" "" is_empty) if(is_empty) message(FATAL_ERROR "HUNTER_INSTALL_PREFIX is empty") endif() if(NOT EXISTS "${HUNTER_INSTALL_PREFIX}") message(FATAL_ERROR "Directory not exists: ${HUNTER_INSTALL_PREFIX}") endif() string(COMPARE EQUAL "${LIST_OF_FILES}" "" is_empty) if(is_empty) message(FATAL_ERROR "LIST_OF_FILES is empty") endif() if(NOT EXISTS "${LIST_OF_FILES}") message(FATAL_ERROR "File not exists: ${LIST_OF_FILES}") endif() string(COMPARE EQUAL "${SHELL_LINK_SCRIPT}" "" is_empty) if(is_empty) message(FATAL_ERROR "SHELL_LINK_SCRIPT is empty") endif() if(NOT EXISTS "${SHELL_LINK_SCRIPT}") message(FATAL_ERROR "File not exists: ${SHELL_LINK_SCRIPT}") endif() string(COMPARE EQUAL "${CELLAR_RAW_DIRECTORY}" "" is_empty) if(is_empty) message(FATAL_ERROR "CELLAR_RAW_DIRECTORY is empty") endif() if(NOT EXISTS "${CELLAR_RAW_DIRECTORY}") message(FATAL_ERROR "Directory not found: ${CELLAR_RAW_DIRECTORY}") endif() string(COMPARE EQUAL "${PYTHON_LINK_SCRIPT}" "" is_empty) if(is_empty) message(FATAL_ERROR "PYTHON_LINK_SCRIPT is empty") endif() if(NOT EXISTS "${PYTHON_LINK_SCRIPT}") message(FATAL_ERROR "File not exists: ${PYTHON_LINK_SCRIPT}") endif() find_package(PythonInterp 3 QUIET) if(PYTHONINTERP_FOUND) message("Link files using Python: ${PYTHON_EXECUTABLE}") set( cmd "${PYTHON_EXECUTABLE}" "${PYTHON_LINK_SCRIPT}" "--list" "${LIST_OF_FILES}" "--cellar" "${CELLAR_RAW_DIRECTORY}" "--dest" "${HUNTER_INSTALL_PREFIX}" ) execute_process( COMMAND ${cmd} WORKING_DIRECTORY "${CELLAR_RAW_DIRECTORY}" RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE error ) if(NOT result EQUAL 0) message( WARNING "Python script failed: ${cmd}, ${result}, ${output}, ${error}" "(may help: https://stackoverflow.com/a/2009505/2288008)" ) else() return() endif() endif() set(shell "/bin/bash") if(EXISTS "${shell}") message("Link files using shell: ${shell}") set(cmd "${shell}" "${SHELL_LINK_SCRIPT}" "${HUNTER_INSTALL_PREFIX}") execute_process( COMMAND ${cmd} WORKING_DIRECTORY "${CELLAR_RAW_DIRECTORY}" RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE error ) if(NOT result EQUAL 0) message( FATAL_ERROR "Shell script failed: ${cmd}, ${result}, ${output}, ${error}" ) endif() return() endif() message("Copy files") file(STRINGS "${LIST_OF_FILES}" files) foreach(x ${files}) set(full_dst_path "${HUNTER_INSTALL_PREFIX}/${x}") get_filename_component(dst_dir "${full_dst_path}" DIRECTORY) file(COPY "${CELLAR_RAW_DIRECTORY}/${x}" DESTINATION "${dst_dir}") endforeach() ================================================ FILE: scripts/link-all.py ================================================ #!/usr/bin/env python3 import argparse import os import sys import multiprocessing parser = argparse.ArgumentParser(description='File link script') parser.add_argument('--list', required=True) parser.add_argument('--cellar', required=True) parser.add_argument('--dest', required=True) cmd_args = parser.parse_args() src_list = [] with open(cmd_args.list, 'r') as f: for line in f: src_list.append(line.strip()) list_len = len(src_list) proc_num = multiprocessing.cpu_count() files_per_job = list_len // proc_num def job(job_index): try: begin_ind = files_per_job * job_index end_ind = files_per_job * (job_index + 1) last_job = (job_index == proc_num - 1) if last_job: end_ind = list_len for i in range(begin_ind, end_ind): filename = src_list[i] link_from = os.path.join(cmd_args.cellar, filename) link_to = os.path.join(cmd_args.dest, filename) os.link(link_from, link_to) return 0 except Exception as exc: print('Exception caught: {}'.format(exc)) return 1 def run_link(): pool = multiprocessing.Pool(processes=proc_num) result = pool.map(job, range(proc_num)) pool.close() pool.join() if 1 in result: sys.exit('Some job failed') if __name__ == '__main__': run_link() ================================================ FILE: scripts/pkgconfig-export-targets.cmake.in ================================================ # ---------------------------------------------------------------------- # Auto creation of CMake targets from pkg-config # # Copyright (C) 2017 Alexandre Pretyman. All rights reserved. # # ---------------------------------------------------------------------- include(hunter_pkgconfig_export_target) hunter_pkgconfig_export_target("@PKG_CONFIG_MODULE@") foreach(_hunter_deps @DEPENDS_ON_PACKAGES@) find_package("${_hunter_deps}" CONFIG REQUIRED) set_property( TARGET "PkgConfig::@PKG_CONFIG_MODULE@" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "PkgConfig::${_hunter_deps}" ) endforeach() ================================================ FILE: scripts/predefined.list ================================================ _ABI64 _ABIO32 _AIX _AIX3 _AIX32 _AIX41 _AIX43 _ARCH_601 _ARCH_603 _ARCH_PPC _ARCH_PWR _ARCH_PWR2 _ATL_VER _BIG_ENDIAN _BYTE_ORDER _CHAR_UNSIGNED _COMPILER_VERSION _CONTROL_FLOW_GUARD _CPPLIB_VER _CPPRTTI _CPPUNWIND _DLL _GLIBCXX_USE_CXX11_ABI _GNU_SOURCE _IA64 _IBMR2 _INTEGRAL_MAX_BITS _ISO_VOLATILE _KERNEL_MODE _LIBCPP_HAS_NO_ASAN _LIBCPP_VERSION _LITTLE_ENDIAN _LP64 _MANAGED _MFC_VER _MIPSEB _MIPSEL _MIPS_ISA_MIPS1 _MIPS_ISA_MIPS2 _MIPS_ISA_MIPS3 _MIPS_ISA_MIPS4 _MRI _MSC_BUILD _MSC_EXTENSIONS _MSC_FULL_VER _MSC_VER _MSVC_LANG _MT _M_ALPHA _M_AMD64 _M_ARM _M_ARM64 _M_ARM_ARMV7VE _M_ARM_FP _M_CEE _M_CEE_PURE _M_CEE_SAFE _M_FP_EXCEPT _M_FP_FAST _M_FP_PRECISE _M_FP_STRICT _M_IA64 _M_IX86 _M_IX86_FP _M_PPC _M_X64 _NATIVE_WCHAR_T_DEFINED _NTO_VERSION _OPENMP _PACC_VER _PA_RISC1_0 _PA_RISC1_1 _PA_RISC2_0 _PDP_ENDIAN _POSIX_SOURCE _POWER _PREFAST_ _R3000 _R4000 _RWSTD_VER _SGI_COMPILER_VERSION _STDC_PREDEF_H _STLPORT_MAJOR _STLPORT_VERSION _SYSTYPE_BSD _SYSTYPE_SVR4 _VC_NODEFAULTLIB _WCHAR_T_DEFINED _WIN32 _WIN32_WINNT _WIN64 _WINRT_DLL _Wp64 _X86_ _XENON _XOPEN_SOURCE _YVALS __370__ __AARCH64EB__ __AARCH64EL__ __AARCH64_SIMD__ __ACCUM_EPSILON__ __ACCUM_FBIT__ __ACCUM_IBIT__ __ACCUM_MAX__ __ACCUM_MIN__ __ALTIVEC__ __ANDROID_API__ __ANDROID__ __APCS_32__ __APPLE_CC__ __APPLE__ __ARM64_ARCH_8__ __ARMEB__ __ARMEL__ __ARM_32BIT_STATE __ARM_64BIT_STATE __ARM_ACLE __ARM_ALIGN_MAX_STACK_PWR __ARM_ARCH __ARM_ARCH_7A__ __ARM_ARCH_7S__ __ARM_ARCH_EXT_IDIV__ __ARM_ARCH_ISA_A64 __ARM_ARCH_ISA_ARM __ARM_ARCH_ISA_THUMB __ARM_ARCH_PROFILE __ARM_ASM_SYNTAX_UNIFIED__ __ARM_EABI__ __ARM_FEATURE_CLZ __ARM_FEATURE_CRYPTO __ARM_FEATURE_DIV __ARM_FEATURE_DSP __ARM_FEATURE_FMA __ARM_FEATURE_LDREX __ARM_FEATURE_QBIT __ARM_FEATURE_SAT __ARM_FEATURE_SIMD32 __ARM_FEATURE_UNALIGNED __ARM_FP __ARM_FP16_FORMAT_IEEE __ARM_NEON __ARM_NEON_FP __ARM_NEON__ __ARM_PCS_AAPCS64 __ARM_PCS_VFP __ARM_SIZEOF_MINIMAL_ENUM __ARM_SIZEOF_WCHAR_T __ARM_VFPV3__ __ARM_VFPV4__ __ATOMIC_ACQUIRE __ATOMIC_ACQ_REL __ATOMIC_CONSUME __ATOMIC_HLE_ACQUIRE __ATOMIC_HLE_RELEASE __ATOMIC_RELAXED __ATOMIC_RELEASE __ATOMIC_SEQ_CST __ATOM__ __AVX2__ __AVX__ __BEOS__ __BFIN__ __BIGGEST_ALIGNMENT__ __BIG_ENDIAN __BIG_ENDIAN__ __BLOCKS__ __BORLANDC__ __BYTE_ORDER __BYTE_ORDER__ __CHAR16_TYPE__ __CHAR32_TYPE__ __CHAR_BIT__ __CHAR_UNSIGNED__ __CLR_VER __CODEGEARC__ __COMO_VERSION__ __COMO__ __COMPILER_VER__ __CONO_VERSION__ __CONSTANT_CFSTRINGS__ __COUNTER__ __CRTL_VER __CWCC__ __CYGWIN__ __DA_FBIT__ __DA_IBIT__ __DBL_DECIMAL_DIG__ __DBL_DENORM_MIN__ __DBL_DIG__ __DBL_EPSILON__ __DBL_HAS_DENORM__ __DBL_HAS_INFINITY__ __DBL_HAS_QUIET_NAN__ __DBL_MANT_DIG__ __DBL_MAX_10_EXP__ __DBL_MAX_EXP__ __DBL_MAX__ __DBL_MIN_10_EXP__ __DBL_MIN_EXP__ __DBL_MIN__ __DCC__ __DEC128_EPSILON__ __DEC128_MANT_DIG__ __DEC128_MAX_EXP__ __DEC128_MAX__ __DEC128_MIN_EXP__ __DEC128_MIN__ __DEC128_SUBNORMAL_MIN__ __DEC32_EPSILON__ __DEC32_MANT_DIG__ __DEC32_MAX_EXP__ __DEC32_MAX__ __DEC32_MIN_EXP__ __DEC32_MIN__ __DEC32_SUBNORMAL_MIN__ __DEC64_EPSILON__ __DEC64_MANT_DIG__ __DEC64_MAX_EXP__ __DEC64_MAX__ __DEC64_MIN_EXP__ __DEC64_MIN__ __DEC64_SUBNORMAL_MIN__ __DECC __DECCXX __DECCXX_VER __DECC_VER __DECIMAL_BID_FORMAT__ __DECIMAL_DIG__ __DEC_EVAL_METHOD__ __DEPRECATED __DMC__ __DQ_FBIT__ __DQ_IBIT__ __DYNAMIC__ __DragonFly__ __ECC __EDG__ __ELF__ __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ __EXCEPTIONS __FINITE_MATH_ONLY__ __FLOAT_WORD_ORDER__ __FLT_DECIMAL_DIG__ __FLT_DENORM_MIN__ __FLT_DIG__ __FLT_EPSILON__ __FLT_EVAL_METHOD__ __FLT_HAS_DENORM__ __FLT_HAS_INFINITY__ __FLT_HAS_QUIET_NAN__ __FLT_MANT_DIG__ __FLT_MAX_10_EXP__ __FLT_MAX_EXP__ __FLT_MAX__ __FLT_MIN_10_EXP__ __FLT_MIN_EXP__ __FLT_MIN__ __FLT_RADIX__ __FMA4__ __FMA__ __FP_FAST_FMA __FP_FAST_FMAF __FP_FAST_FMAL __FRACT_EPSILON__ __FRACT_FBIT__ __FRACT_IBIT__ __FRACT_MAX__ __FRACT_MIN__ __FUNCDNAME__ __FUNCSIG__ __FXSR__ __FreeBSD__ __FreeBSD_version __GCCXML__ __GCC_ASM_FLAG_OUTPUTS__ __GCC_ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_TEST_AND_SET_TRUEVAL __GCC_ATOMIC_WCHAR_T_LOCK_FREE __GCC_HAVE_DWARF2_CFI_ASM __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 __GCC_IEC_559 __GCC_IEC_559_COMPLEX __GHS_VERSION_NUMBER__ __GLIBCPP__ __GLIBCXX_BITSIZE_INT_N_0 __GLIBCXX_TYPE_INT_N_0 __GLIBCXX__ __GLIBC__ __GNUC_GNU_INLINE__ __GNUC_LIBSTD_MINOR__ __GNUC_LIBSTD__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ __GNUC_STDC_INLINE__ __GNUC__ __GNUG__ __GNU_LIBRARY__ __GXX_ABI_VERSION __GXX_EXPERIMENTAL_CXX0X__ __GXX_RTTI __GXX_TYPEINFO_EQUALITY_INLINE __GXX_WEAK__ __HAIKU__ __HA_FBIT__ __HA_IBIT__ __HIGHC__ __HPPA11__ __HPPA20__ __HPPA__ __HP_aCC __HQ_FBIT__ __HQ_IBIT__ __I86__ __IA64__ __IAR_SYSTEMS_ICC__ __IBMCPP__ __ICC __ICL __INT16_C __INT16_MAX__ __INT16_TYPE__ __INT32_C __INT32_MAX__ __INT32_TYPE__ __INT64_C __INT64_C_SUFFIX__ __INT64_MAX__ __INT64_TYPE__ __INT8_C __INT8_MAX__ __INT8_TYPE__ __INTEL_COMPILER __INTEL__ __INTMAX_C __INTMAX_MAX__ __INTMAX_TYPE__ __INTMAX_WIDTH__ __INTPTR_MAX__ __INTPTR_TYPE__ __INTPTR_WIDTH__ __INT_FAST16_MAX__ __INT_FAST16_TYPE__ __INT_FAST32_MAX__ __INT_FAST32_TYPE__ __INT_FAST64_MAX__ __INT_FAST64_TYPE__ __INT_FAST8_MAX__ __INT_FAST8_TYPE__ __INT_LEAST16_MAX__ __INT_LEAST16_TYPE__ __INT_LEAST32_MAX__ __INT_LEAST32_TYPE__ __INT_LEAST64_MAX__ __INT_LEAST64_TYPE__ __INT_LEAST8_MAX__ __INT_LEAST8_TYPE__ __INT_MAX__ __KCC __LACCUM_EPSILON__ __LACCUM_FBIT__ __LACCUM_IBIT__ __LACCUM_MAX__ __LACCUM_MIN__ __LDBL_DENORM_MIN__ __LDBL_DIG__ __LDBL_EPSILON__ __LDBL_HAS_DENORM__ __LDBL_HAS_INFINITY__ __LDBL_HAS_QUIET_NAN__ __LDBL_MANT_DIG__ __LDBL_MAX_10_EXP__ __LDBL_MAX_EXP__ __LDBL_MAX__ __LDBL_MIN_10_EXP__ __LDBL_MIN_EXP__ __LDBL_MIN__ __LFRACT_EPSILON__ __LFRACT_FBIT__ __LFRACT_IBIT__ __LFRACT_MAX__ __LFRACT_MIN__ __LIBCOMO__ __LIBREL__ __LITTLE_ENDIAN __LITTLE_ENDIAN__ __LLACCUM_EPSILON__ __LLACCUM_FBIT__ __LLACCUM_IBIT__ __LLACCUM_MAX__ __LLACCUM_MIN__ __LLFRACT_EPSILON__ __LLFRACT_FBIT__ __LLFRACT_IBIT__ __LLFRACT_MAX__ __LLFRACT_MIN__ __LONG_LONG_MAX__ __LONG_MAX__ __LP64__ __MACH__ __MIC__ __MINGW32_VERSION_MAJOR __MINGW32_VERSION_MINOR __MINGW32__ __MINGW64_VERSION_MAJOR __MINGW64_VERSION_MINOR __MINGW64__ __MIPSEB __MIPSEB__ __MIPSEL __MIPSEL__ __MIPS_ISA2__ __MIPS_ISA3__ __MIPS_ISA4__ __MIPS__ __MMX__ __MRC__ __MSIPL_COMPILE_H __MSL_CPP__ __MSL__ __MSVC_RUNTIME_CHECKS __MWERKS__ __NETBSD__ __NETBSD_version __NO_MATH_INLINES __NetBSD_Version __NetBSD__ __OBJC__ __ORDER_BIG_ENDIAN__ __ORDER_LITTLE_ENDIAN__ __ORDER_PDP_ENDIAN__ __OS400__ __OpenBSD__ __PA7100__ __PA8000__ __PATHCC__ __PDP_ENDIAN __PGI __PGIC_MINOR__ __PGIC_PATCHLEVEL__ __PGIC__ __PIC__ __PIE__ __POINTER_WIDTH__ __POWERPC__ __PPCBROADWAY__ __PPCGECKO__ __PRAGMA_REDEFINE_EXTNAME __PTRDIFF_MAX__ __PTRDIFF_TYPE__ __PTRDIFF_WIDTH__ __QNXNTO__ __QNX__ __QQ_FBIT__ __QQ_IBIT__ __REGISTER_PREFIX__ __RISC2_0__ __SACCUM_EPSILON__ __SACCUM_FBIT__ __SACCUM_IBIT__ __SACCUM_MAX__ __SACCUM_MIN__ __SA_FBIT__ __SA_IBIT__ __SCHAR_MAX__ __SEG_FS __SEG_GS __SFRACT_EPSILON__ __SFRACT_FBIT__ __SFRACT_IBIT__ __SFRACT_MAX__ __SFRACT_MIN__ __SGI_STL __SGI_STL_PORT __SH3__ __SH4__ __SH5__ __SHRT_MAX__ __SIG_ATOMIC_MAX__ __SIG_ATOMIC_MIN__ __SIG_ATOMIC_TYPE__ __SIG_ATOMIC_WIDTH__ __SIZEOF_DOUBLE__ __SIZEOF_FLOAT128__ __SIZEOF_FLOAT80__ __SIZEOF_FLOAT__ __SIZEOF_INT128__ __SIZEOF_INT__ __SIZEOF_LONG_DOUBLE__ __SIZEOF_LONG_LONG__ __SIZEOF_LONG__ __SIZEOF_POINTER__ __SIZEOF_PTRDIFF_T__ __SIZEOF_SHORT__ __SIZEOF_SIZE_T__ __SIZEOF_WCHAR_T__ __SIZEOF_WINT_T__ __SIZE_MAX__ __SIZE_TYPE__ __SIZE_WIDTH__ __SQ_FBIT__ __SQ_IBIT__ __SSE2_MATH__ __SSE2__ __SSE3__ __SSE4A__ __SSE4_1__ __SSE4_2__ __SSE_MATH__ __SSE__ __SSP_STRONG__ __SSP__ __SSSE3__ __STDCPP_DEFAULT_NEW_ALIGNMENT__ __STDCPP_STRICT_POINTER_SAFETY__ __STDCPP_THREADS__ __STDC_HOSTED__ __STDC_IEC_559_COMPLEX__ __STDC_IEC_559__ __STDC_ISO_10646__ __STDC_MB_MIGHT_NEQ_WC__ __STDC_NO_THREADS__ __STDC_UTF_16__ __STDC_UTF_32__ __STDC_VERSION__ __STDC__ __STD_RWCOMPILER_H__ __STL_CONFIG_H __SUNPRO_C __SUNPRO_CC __SVR4 __SYSC_ZARCH__ __SYSC__ __TARGET_ARCH_ARM __TARGET_ARCH_THUMB __TARGET_LIB__ __TA_FBIT__ __TA_IBIT__ __THUMBEB__ __THUMBEL__ __THUMB_INTERWORK__ __THW_370__ __THW_INTEL__ __THW_RS6000 __TOS_AIX__ __TOS_WIN__ __TQ_FBIT__ __TQ_IBIT__ __TenDRA__ __UACCUM_EPSILON__ __UACCUM_FBIT__ __UACCUM_IBIT__ __UACCUM_MAX__ __UACCUM_MIN__ __UCLIBC__ __UDA_FBIT__ __UDA_IBIT__ __UDQ_FBIT__ __UDQ_IBIT__ __UFRACT_EPSILON__ __UFRACT_FBIT__ __UFRACT_IBIT__ __UFRACT_MAX__ __UFRACT_MIN__ __UHA_FBIT__ __UHA_IBIT__ __UHQ_FBIT__ __UHQ_IBIT__ __UINT16_C __UINT16_MAX__ __UINT16_TYPE__ __UINT32_C __UINT32_MAX__ __UINT32_TYPE__ __UINT64_C __UINT64_MAX__ __UINT64_TYPE__ __UINT8_C __UINT8_MAX__ __UINT8_TYPE__ __UINTMAX_C __UINTMAX_MAX__ __UINTMAX_TYPE__ __UINTPTR_MAX__ __UINTPTR_TYPE__ __UINT_FAST16_MAX__ __UINT_FAST16_TYPE__ __UINT_FAST32_MAX__ __UINT_FAST32_TYPE__ __UINT_FAST64_MAX__ __UINT_FAST64_TYPE__ __UINT_FAST8_MAX__ __UINT_FAST8_TYPE__ __UINT_LEAST16_MAX__ __UINT_LEAST16_TYPE__ __UINT_LEAST32_MAX__ __UINT_LEAST32_TYPE__ __UINT_LEAST64_MAX__ __UINT_LEAST64_TYPE__ __UINT_LEAST8_MAX__ __UINT_LEAST8_TYPE__ __ULACCUM_EPSILON__ __ULACCUM_FBIT__ __ULACCUM_IBIT__ __ULACCUM_MAX__ __ULACCUM_MIN__ __ULFRACT_EPSILON__ __ULFRACT_FBIT__ __ULFRACT_IBIT__ __ULFRACT_MAX__ __ULFRACT_MIN__ __ULLACCUM_EPSILON__ __ULLACCUM_FBIT__ __ULLACCUM_IBIT__ __ULLACCUM_MAX__ __ULLACCUM_MIN__ __ULLFRACT_EPSILON__ __ULLFRACT_FBIT__ __ULLFRACT_IBIT__ __ULLFRACT_MAX__ __ULLFRACT_MIN__ __UQQ_FBIT__ __UQQ_IBIT__ __USACCUM_EPSILON__ __USACCUM_FBIT__ __USACCUM_IBIT__ __USACCUM_MAX__ __USACCUM_MIN__ __USA_FBIT__ __USA_IBIT__ __USER_LABEL_PREFIX__ __USFRACT_EPSILON__ __USFRACT_FBIT__ __USFRACT_IBIT__ __USFRACT_MAX__ __USFRACT_MIN__ __USING_SJLJ_EXCEPTIONS__ __USQ_FBIT__ __USQ_IBIT__ __UTA_FBIT__ __UTA_IBIT__ __UTQ_FBIT__ __UTQ_IBIT__ __VECTOR4DOUBLE__ __VEC__ __VERSION__ __VFP_FP__ __VMS __VMS_VER __VSX__ __WATCOMC__ __WCHAR_MAX__ __WCHAR_MIN__ __WCHAR_TYPE__ __WCHAR_UNSIGNED__ __WCHAR_WIDTH__ __WIN32__ __WINDOWS__ __WINT_MAX__ __WINT_MIN__ __WINT_TYPE__ __WINT_WIDTH__ __XOP__ __aarch64__ __alpha __alpha__ __alpha_ev4__ __alpha_ev5__ __alpha_ev6__ __amd64 __amd64__ __amigaos__ __apple_build_version__ __arm __arm64 __arm64__ __arm__ __bfin__ __block __bsdi__ __clang__ __clang_major__ __clang_minor__ __clang_patchlevel__ __clang_version__ __code_model_small__ __convex__ __convex_c1__ __convex_c2__ __convex_c32__ __convex_c34__ __convex_c38__ __core2 __core2__ __cplusplus __cplusplus_cli __cplusplus_winrt __cpp_aggregate_nsdmi __cpp_alias_templates __cpp_attributes __cpp_binary_literals __cpp_constexpr __cpp_decltype __cpp_decltype_auto __cpp_delegating_constructors __cpp_digit_separators __cpp_exceptions __cpp_generic_lambdas __cpp_hex_float __cpp_inheriting_constructors __cpp_init_captures __cpp_initializer_lists __cpp_lambdas __cpp_nsdmi __cpp_range_based_for __cpp_raw_strings __cpp_ref_qualifiers __cpp_return_type_deduction __cpp_rtti __cpp_runtime_arrays __cpp_rvalue_reference __cpp_sized_deallocation __cpp_static_assert __cpp_unicode_characters __cpp_unicode_literals __cpp_user_defined_literals __cpp_variable_templates __cpp_variadic_templates __embedded_cplusplus __ghs __ghs__ __gnu_linux__ __hppa __hppa__ __hpux __i386 __i386__ __i486__ __i586__ __i686__ __ia64 __ia64__ __itanium__ __k8 __k8__ __linux __linux__ __llvm__ __m68k__ __mc68000 __mc68000__ __mc68010 __mc68010__ __mc68020 __mc68020__ __mc68030 __mc68030__ __mc68040 __mc68040__ __mc68060 __mc68060__ __mips __mips__ __nocona __nocona__ __pic__ __pie__ __powerpc __powerpc__ __ppc601__ __ppc603__ __ppc604__ __ppc__ __private_extern__ __s390__ __s390x__ __sgi __sh1__ __sh2__ __sh3__ __sh__ __sparc __sparc__ __sparcv8 __sparcv9 __strong __sun __svr4__ __sysv__ __thumb2__ __thumb__ __tune_core2__ __tune_nocona__ __unix __unix__ __unsafe_unretained __weak __x86_64 __x86_64__ __xlC__ __xlc__ _hpux ================================================ FILE: scripts/try-copy-license.cmake ================================================ # Copyright (c) 2015 Aaditya Kalsi # Copyright (c) 2017 Ruslan Baratov # All rights reserved. cmake_minimum_required(VERSION 3.0) string(COMPARE EQUAL "${srcdir}" "" is_empty) if(is_empty) message(FATAL_ERROR "'srcdir' should not be empty") endif() string(COMPARE EQUAL "${dstdir}" "" is_empty) if(is_empty) message(FATAL_ERROR "'dstdir' should not be empty") endif() set(licenses "${HUNTER_INSTALL_LICENSE_FILES}") string(COMPARE NOTEQUAL "${licenses}" "" explicit_licenses) file(MAKE_DIRECTORY "${dstdir}") if(NOT EXISTS "${dstdir}") message(FATAL_ERROR "Can't create directory: '${dstdir}'") endif() if(NOT IS_DIRECTORY "${dstdir}") message(FATAL_ERROR "Is not a directory: '${dstdir}'") endif() # Use set explicit license files for the package # Testing variants: # * Eigen "HUNTER_INSTALL_LICENSE_FILES=COPYING.MPL2" (good) # * Eigen "HUNTER_INSTALL_LICENSE_FILES=COPYING.BSD;COPYING.GPL;COPYING.LGPL;COPYING.MINPACK;COPYING.MPL2" (good) # * Eigen "HUNTER_INSTALL_LICENSE_FILES=COPYING.BSD;COPYING.GPL;COPYING.LGPL;COPYING.XXX;COPYING.MPL2" (bad) # * Eigen no HUNTER_INSTALL_LICENSE_FILES (bad, no default license) if(explicit_licenses) foreach(x ${licenses}) if(NOT EXISTS "${srcdir}/${x}") message(FATAL_ERROR "File not found: '${srcdir}/${x}'") endif() file(COPY "${srcdir}/${x}" DESTINATION "${dstdir}") endforeach() return() endif() # Try standard names foreach(x "LICENSE" "LICENSE.txt" "COPYING" "COPYING.txt" "license" "license.txt" "copying" "copying.txt") if(EXISTS "${srcdir}/${x}") file(COPY "${srcdir}/${x}" DESTINATION "${dstdir}") return() endif() endforeach() # If no standard name found try to glob LICENSE* # Testing variants: # * Boost file(GLOB filelist "${srcdir}/LICENSE*") foreach(x ${filelist}) if(NOT IS_DIRECTORY "${x}") file(COPY "${x}" DESTINATION "${dstdir}") endif() endforeach() ================================================ FILE: scripts/upload-cache-to-github.py ================================================ #!/usr/bin/env python3 from __future__ import print_function import argparse import hashlib import json import os import requests import sys import time if os.getenv('HUNTER_GIT_EXECUTABLE'): os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = os.getenv('HUNTER_GIT_EXECUTABLE') try: import git except ImportError as exc: print("Import failed with error: {}".format(exc)) print("Possible fixes:") print(" * Install gitpython module: 'pip install gitpython'") print(" * Set environment variable HUNTER_GIT_EXECUTABLE") sys.exit(1) class Error(Exception): pass def sleep_time(attempt): if attempt <= 0: raise Exception('Unexpected') if attempt == 1: return 0 if attempt == 2: return 15 if attempt == 3: return 60 if attempt == 4: return 90 if attempt == 5: return 300 return 1200 def retry(func_in): def func_out(*args, **kwargs): retry_max = 10 i = 0 while True: i = i + 1 try: return func_in(*args, **kwargs) except Error as err: # Treat Errors as fatal and do not retry. # Also explicitly flush message to avoid "no output" issue on some CIs. print('Error:\n {}'.format(err)) sys.stdout.flush() raise err except Exception as exc: if i > retry_max: raise exc print('Operation failed. Exception:\n {}'.format(exc)) sec = sleep_time(i) print('Retry #{} (of {}) after {} seconds'.format(i, retry_max, sec)) sys.stdout.flush() time.sleep(sec) return func_out class Github: def __init__(self, username, password, repo_owner, repo): self.repo_owner = repo_owner self.repo = repo self.username = username self.password = password self.auth = requests.auth.HTTPBasicAuth(username, password) self.simple_request() @retry def simple_request(self): print('Processing simple request') r = requests.get('https://api.github.com', auth=self.auth) if not r.ok: sys.exit('Simple request fails. Check your password.') limit = int(r.headers['X-RateLimit-Remaining']) print('GitHub Limit: {}'.format(limit)) if limit == 0: raise Exception('GitHub limit is 0') print('Simple request pass') @retry def get_release_by_tag(self, tagname): print('Get release-id by tag `{}`'.format(tagname)) # https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name # GET /repos/:owner/:repo/releases/tags/:tag url = 'https://api.github.com/repos/{}/{}/releases/tags/{}'.format( self.repo_owner, self.repo, tagname ) r = requests.get(url, auth=self.auth) if r.status_code == 404: # Create release if not exists # https://developer.github.com/v3/repos/releases/#create-a-release # POST /repos/:owner/:repo/releases post_url = 'https://api.github.com/repos/{}/{}/releases'.format( self.repo_owner, self.repo, ) tag_data = "{" + '"tag_name": "{}"'.format(tagname) + "}" r = requests.post(post_url, data=tag_data, auth=self.auth) repo_name = "https://github.com/{}/{}".format( self.repo_owner, self.repo ) if r.status_code == 404: raise Error( "Repository not found '{}' or user '{}' has no access to it". format(repo_name, self.username) ) if r.status_code == 422: raise Error( "Please create at least one file in repository '{}'". format(repo_name) ) if not r.status_code == 201: raise Error("Unexpected status code: {}".format(r.status_code)) if not r.ok: raise Error("Can't create release tag {}".format(tagname)) r = requests.get(url, auth=self.auth) if not r.ok: raise Exception( 'Get release id failed. Status code: {}. Requested url: {}'.format( r.status_code, url)) release_id = r.json()['id'] upload_url = r.json()['upload_url'] uri_template_vars = '{?name,label}' if uri_template_vars not in upload_url: raise Exception('Unsupported upload URI template: {}'.format(upload_url)) upload_url = upload_url.replace(uri_template_vars, '?name={}') print('Release id: {}'.format(release_id)) print('Upload URL: {}'.format(upload_url)) return release_id, upload_url @retry def find_asset_id_by_name(self, release_id, name): # https://developer.github.com/v3/repos/releases/#list-assets-for-a-release # GET /repos/:owner/:repo/releases/:id/assets page_number = 1 keep_searching = True while keep_searching: url = 'https://api.github.com/repos/{}/{}/releases/{}/assets?page={}'.format( self.repo_owner, self.repo, release_id, page_number ) print('Requesting URL: {}'.format(url)) r = requests.get(url, auth=self.auth) if not r.ok: raise Exception('Getting list of assets failed. Requested url: {}'.format(url)) json = r.json() for x in json: if name == x['name']: return x['id'] if not json: keep_searching = False page_number = page_number + 1 return None @retry def delete_asset_by_id(self, asset_id, asset_name): # https://developer.github.com/v3/repos/releases/#delete-a-release-asset # DELETE /repos/:owner/:repo/releases/assets/:id url = 'https://api.github.com/repos/{}/{}/releases/assets/{}'.format( self.repo_owner, self.repo, asset_id ) r = requests.delete(url, auth=self.auth) if r.status_code == 204: print('Asset removed: {}'.format(asset_name)) else: raise Exception('Deletion of asset failed: {}'.format(asset_name)) def delete_asset_if_exists(self, release_id, asset_name): asset_id = self.find_asset_id_by_name(release_id, asset_name) if not asset_id: print('Asset not exists: {}'.format(asset_name)) return self.delete_asset_by_id(asset_id, asset_name) def upload_bzip_once(self, url, local_path): headers = {'Content-Type': 'application/x-bzip2'} file_to_upload = open(local_path, 'rb') r = requests.post(url, data=file_to_upload, headers=headers, auth=self.auth) if not r.ok: raise Exception('Upload of file failed') @retry def upload_bzip(self, url, local_path, release_id, asset_name): print('Uploading:\n {}\n -> {}'.format(local_path, url)) try: self.upload_bzip_once(url, local_path) except Exception as exc: print('Exception catched while uploading, removing asset...') self.delete_asset_if_exists(release_id, asset_name) raise exc def upload_raw_file(self, local_path): asset_name = hashlib.sha1(open(local_path, 'rb').read()).hexdigest() tagname = 'cache-{}'.format(asset_name[0:7]) asset_name = asset_name + '.tar.bz2' release_id, upload_url = self.get_release_by_tag(tagname) # https://developer.github.com/v3/repos/releases/#upload-a-release-asset # POST to upload_url received in the release description # in get_release_by_tag() url = upload_url.format(asset_name) self.upload_bzip(url, local_path, release_id, asset_name) class CacheEntry: def __init__(self, cache_done_path, cache_dir): self.cache_dir = cache_dir self.cache_raw = os.path.join(self.cache_dir, 'raw') self.cache_done_path = cache_done_path if not os.path.exists(cache_done_path): raise Exception('File not exists: {}'.format(cache_done_path)) self.cache_done_dir = os.path.dirname(self.cache_done_path) self.from_server = os.path.join(self.cache_done_dir, 'from.server') self.cache_sha1 = os.path.join(self.cache_done_dir, 'cache.sha1') def entry_from_server(self): return os.path.exists(self.from_server) def upload_raw(self, github): sha1 = open(self.cache_sha1, 'r').read() if sha1 == '': sys.exit('File with no content: {}'.format(self.cache_sha1)) raw = os.path.join(self.cache_raw, sha1 + '.tar.bz2') if os.path.exists(raw): github.upload_raw_file(raw) # else: # FIXME (old GitHub API upload): https://travis-ci.org/ingenue/hunter/jobs/347888167 # New Git-based upload: 'from.server' not present for old cache def touch_from_server(self): open(self.from_server, 'w') class Cache: def __init__(self, cache_dir): self.cache_meta = os.path.join(cache_dir, 'meta') self.repo = git.Repo.init(self.cache_meta) self.entries = self.create_entries(cache_dir) self.remove_entries_from_server() def create_entries(self, cache_dir): print('Searching for CACHE.DONE files in directory:\n {}\n'.format(cache_dir)) entries = [] for x in self.repo.untracked_files: if x.endswith('CACHE.DONE'): entries.append(CacheEntry(os.path.join(self.cache_meta, x), cache_dir)) print('Found {} files'.format(len(entries))) return entries def remove_entries_from_server(self): new_entries = [] for i in self.entries: if not i.entry_from_server(): new_entries.append(i) self.entries = new_entries def upload_raw(self, github): for i in self.entries: i.upload_raw(github) def make_commit_message(self): message = 'Uploading cache info\n\n' env_list = [] job_url = '' if os.getenv('TRAVIS') == 'true': # * https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables message += 'Travis:\n' job_url = 'https://travis-ci.org/{}/jobs/{}'.format( os.getenv('TRAVIS_REPO_SLUG'), os.getenv('TRAVIS_JOB_ID') ) env_list += [ 'TRAVIS_BRANCH', 'TRAVIS_BUILD_ID', 'TRAVIS_BUILD_NUMBER', 'TRAVIS_JOB_ID', 'TRAVIS_JOB_NUMBER', 'TRAVIS_OS_NAME', 'TRAVIS_REPO_SLUG' ] if os.getenv('APPVEYOR') == 'True': # * http://www.appveyor.com/docs/environment-variables message += 'AppVeyor:\n' job_url = 'https://ci.appveyor.com/project/{}/{}/build/{}/job/{}'.format( os.getenv('APPVEYOR_ACCOUNT_NAME'), os.getenv('APPVEYOR_PROJECT_SLUG'), os.getenv('APPVEYOR_BUILD_VERSION'), os.getenv('APPVEYOR_JOB_ID') ) env_list += [ 'APPVEYOR_ACCOUNT_NAME', 'APPVEYOR_PROJECT_ID', 'APPVEYOR_PROJECT_NAME', 'APPVEYOR_PROJECT_SLUG', 'APPVEYOR_BUILD_ID', 'APPVEYOR_BUILD_NUMBER', 'APPVEYOR_BUILD_VERSION', 'APPVEYOR_JOB_ID', 'APPVEYOR_JOB_NAME', 'APPVEYOR_REPO_BRANCH' ] # Store some info about build for env_name in env_list: env_value = os.getenv(env_name) if env_value: message += ' {}: {}\n'.format(env_name, env_value) if job_url: message += '\n Job URL: {}\n'.format(job_url) return message def try_to_push(self, main_remote, main_remote_url_pull, github): try: fetch_result = main_remote.pull( allow_unrelated_histories=True, strategy='recursive', strategy_option='ours', rebase=True, depth=1 ) for x in fetch_result: if x.flags & x.REJECTED: print('Pull rejected') return False if x.flags & x.ERROR: print('Pull error') return False except Exception as exc: print("Pull failed: {}".format(exc)) return False try: main_remote.set_url( 'https://{}:{}@github.com/{}/{}'.format( github.username, github.password, github.repo_owner, github.repo ) ) push_result = main_remote.push() main_remote.set_url(main_remote_url_pull) for x in push_result: if x.flags & x.ERROR: print('Push error') return False if x.flags & x.REJECTED: print('Push rejected') return False if x.flags & x.REMOTE_FAILURE: print('Push remote failure') return False if x.flags & x.REMOTE_REJECTED: print('Push remote rejected') return False except: # No exceptions expected, exit to avoid leakage of token sys.exit('Unexpected exception') return True def upload_meta(self, github, cache_dir): config = self.repo.config_writer() config.set_value( "user", "email", "{}@users.noreply.github.com".format(github.username) ) config.set_value("user", "name", github.username) config.release() if self.repo.is_dirty(untracked_files=True): print('Adding untracked files:') add_list = [] for x in self.repo.untracked_files: to_add = False if x.endswith('toolchain.info'): to_add = True elif x.endswith('args.cmake'): to_add = True elif x.endswith('types.info'): to_add = True elif x.endswith('internal_deps.id'): to_add = True elif x.endswith('basic-deps.info'): to_add = True elif x.endswith('basic-deps.DONE'): to_add = True elif x.endswith('cache.sha1'): to_add = True elif x.endswith('deps.info'): to_add = True elif x.endswith('CACHE.DONE'): to_add = True elif x.endswith('SHA1'): to_add = True if to_add: print(' * {}'.format(x)) add_list.append(x) sys.stdout.flush() self.repo.index.add(add_list) self.repo.index.commit(self.make_commit_message()) main_branch_found = False for branch in self.repo.branches: if branch.name == 'master': main_branch_found = True if not main_branch_found: self.repo.git.branch('master') main_remote_found = False for remote in self.repo.remotes: if remote.name == 'origin': main_remote_found = True main_remote = remote main_remote_url_pull = 'https://github.com/{}/{}'.format( github.repo_owner, github.repo ) if not main_remote_found: main_remote = self.repo.create_remote('origin', main_remote_url_pull) retry_max = 10 fetch_ok = False for i in range(1, retry_max): try: if fetch_ok: break print('Fetch remote (attempt #{})'.format(i)) sys.stdout.flush() main_remote.fetch(depth=1) fetch_ok = True except Exception as exc: print('Exception {}'.format(exc)) if not fetch_ok: sys.exit('Fetch failed') self.repo.heads.master.set_tracking_branch(main_remote.refs.master) success = False for i in range(1, retry_max): print("Attempt #{}".format(i)) success = self.try_to_push(main_remote, main_remote_url_pull, github) if success: break sec = sleep_time(i) print('Retry #{} (of {}) after {} seconds'.format(i, retry_max, sec)) sys.stdout.flush() time.sleep(sec) if success: print("Done") else: sys.exit("Can't push") def touch_from_server(self): for i in self.entries: i.touch_from_server() parser = argparse.ArgumentParser( description='Script for uploading Hunter cache files to GitHub' ) parser.add_argument( '--username', required=True, help='Username' ) parser.add_argument( '--password', required=True, help='Password' ) parser.add_argument( '--repo-owner', required=True, help='Repository owner' ) parser.add_argument( '--repo', required=True, help='Repository name' ) parser.add_argument( '--cache-dir', required=True, help='Hunter cache directory, e.g. /home/user/.hunter/_Base/Cache' ) args = parser.parse_args() cache_dir = os.path.normpath(args.cache_dir) # Some tests don't produce cache for some toolchains: # * https://travis-ci.org/ingenue/hunter/jobs/185550289 if not os.path.exists(cache_dir): print("*** WARNING *** Cache directory '{}' not found, skipping...".format(cache_dir)) sys.exit() if not os.path.isdir(cache_dir): raise Exception('Not a directory: {}'.format(cache_dir)) if os.path.split(cache_dir)[1] != 'Cache': raise Exception('Cache directory path should ends with Cache: {}'.format(cache_dir)) cache = Cache(cache_dir) password = args.password if password == '' or password is None: raise Exception('No password provided') github = Github( username = args.username, password = password, repo_owner = args.repo_owner, repo = args.repo ) cache.upload_raw(github) cache.upload_meta(github, cache_dir) print('Touch from.server files') cache.touch_from_server() ================================================ FILE: utils/git-hooks/commit-msg ================================================ #!/usr/bin/env python #!/usr/bin/env python from difflib import unified_diff as differ import os from subprocess import check_output as call import sys import shutil def read(filepath): mode = "b" if sys.platform.startswith("win") else "" data = "" with open(filepath, "rU" + mode) as fd: data = fd.read() return data def get_current_revision(): cmd = "git rev-parse --verify HEAD".split(" ") gitrev = "".join(r.strip() for r in call(cmd).split("\n") if r.strip()) return gitrev def get_commit_filename(): commit_filename = sys.argv[1] return commit_filename def get_project_path(): cmd = "git rev-parse --show-toplevel".split(" ") project_path = "".join(p.strip() for p in call(cmd).split("\n") if p.strip()) return project_path def get_hooks_path(): gitpath = get_project_path() hookspath = os.path.join(gitpath, 'utils', 'git-hooks') if not hookspath: hookspath = os.getcwd() return hookspath def get_subhooks(hookspath, gitpath, fname): sub_hooks = [] for root, folders, files in os.walk(hookspath): for filename in files: hookpath = os.path.join(gitpath, root, filename) if filename.startswith(fname) and filename != fname: sub_hooks.append(hookpath) return sub_hooks def check_for_update(): fname = os.path.basename(__file__) gitpath = get_project_path() old_path = os.path.join(gitpath, '.git', 'hooks', fname) new_path = os.path.join(gitpath, 'utils', 'git-hooks', fname) old_data = read(old_path) new_data = read(new_path) diff = list(differ(old_data.splitlines(), new_data.splitlines(), old_path, new_path)) changed = '\n'.join(diff) if changed: shutil.copyfile(new_path, old_path) shutil.copymode(new_path, old_path) shutil.copystat(new_path, old_path) print >> sys.stderr, "%s updated. Please re-run." % fname sys.exit(1) def run_sub_hooks(): fname = os.path.basename(__file__) gitrev = get_current_revision() git_commit_filename = get_commit_filename() gitpath = get_project_path() hookspath = get_hooks_path() sub_hooks = get_subhooks(hookspath, gitpath, fname) for hookpath in sub_hooks: fpath = os.path.join(gitpath, git_commit_filename) cmd = [hookpath, fpath] output = call(cmd) if output: print output if __name__ == "__main__": check_for_update() run_sub_hooks() ================================================ FILE: utils/git-hooks/commit-msg_check-spelling ================================================ #!/bin/bash ASPELL=$(which aspell) if [ $? -ne 0 ]; then echo "Aspell not installed - unable to check spelling" >&2 exit 0 fi AWK=$(which awk) if [ $? -ne 0 ]; then echo "Awk not installed - unable to filter spelling errors" >&2 WORDS=$($ASPELL list < "$1") else WORDS=$($ASPELL list < "$1" | $AWK '!_[$0]++') fi if [ -n "$WORDS" ]; then echo "Possible spelling errors found in commit message: " $WORDS >&2 echo "To fix, use: git commit --amend" fi exit 0 ================================================ FILE: utils/git-hooks/cpplint/__init__.py ================================================ ================================================ FILE: utils/git-hooks/cpplint/cpplint.py ================================================ #!/usr/bin/env python # # Copyright (c) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Here are some issues that I've had people identify in my code during reviews, # that I think are possible to flag automatically in a lint tool. If these were # caught by lint, it would save time both for myself and that of my reviewers. # Most likely, some of these are beyond the scope of the current lint framework, # but I think it is valuable to retain these wish-list items even if they cannot # be immediately implemented. # # Suggestions # ----------- # - Check for no 'explicit' for multi-arg ctor # - Check for boolean assign RHS in parens # - Check for ctor initializer-list colon position and spacing # - Check that if there's a ctor, there should be a dtor # - Check accessors that return non-pointer member variables are # declared const # - Check accessors that return non-const pointer member vars are # *not* declared const # - Check for using public includes for testing # - Check for spaces between brackets in one-line inline method # - Check for no assert() # - Check for spaces surrounding operators # - Check for 0 in pointer context (should be NULL) # - Check for 0 in char context (should be '\0') # - Check for camel-case method name conventions for methods # that are not simple inline getters and setters # - Check that base classes have virtual destructors # put " // namespace" after } that closes a namespace, with # namespace's name after 'namespace' if it is named. # - Do not indent namespace contents # - Avoid inlining non-trivial constructors in header files # include base/basictypes.h if DISALLOW_EVIL_CONSTRUCTORS is used # - Check for old-school (void) cast for call-sites of functions # ignored return value # - Check gUnit usage of anonymous namespace # - Check for class declaration order (typedefs, consts, enums, # ctor(s?), dtor, friend declarations, methods, member vars) # """Does google-lint on c++ files. The goal of this script is to identify places in the code that *may* be in non-compliance with google style. It does not attempt to fix up these problems -- the point is to educate. It does also not attempt to find all problems, or to ensure that everything it does find is legitimately a problem. In particular, we can get very confused by /* and // inside strings! We do a small hack, which is to ignore //'s with "'s after them on the same line, but it is far from perfect (in either direction). """ import codecs import getopt import math # for log import os import re import sre_compile import string import sys import unicodedata EXTENSIONS = ['c', 'cc', 'cpp', 'cxx', 'c++', 'h', 'hpp', 'hxx', 'h++'] _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [file] ... The style guidelines this tries to follow are those in http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. This will miss some errors, and is not a substitute for a code review. To suppress false-positive errors of a certain category, add a 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) suppresses errors of all categories on that line. The files passed in will be linted; at least one file must be provided. Linted extensions are %s. Other file types will be ignored. Flags: output=vs7 By default, the output is formatted to ease emacs parsing. Visual Studio compatible output (vs7) may also be used. Other formats are unsupported. verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. filter=-x,+y,... Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed. (Category names are printed with the message and look like "[whitespace/indent]".) Filters are evaluated left to right. "-FOO" and "FOO" means "do not print categories that start with FOO". "+FOO" means "do print categories that start with FOO". Examples: --filter=-whitespace,+whitespace/braces --filter=whitespace,runtime/printf,+runtime/printf_format --filter=-,+build/include_what_you_use To see a list of all the categories used in cpplint, pass no arg: --filter= counting=total|toplevel|detailed The total number of errors found is always printed. If 'toplevel' is provided, then the count of errors in each of the top-level categories like 'build' and 'whitespace' will also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. """ % (EXTENSIONS) # We categorize each error message we print. Here are the categories. # We want an explicit list so we can list them all in cpplint --filter=. # If you add a new error message with a new category, add it to the list # here! cpplint_unittest.py should tell you if you forget to do this. # \ used for clearer layout -- pylint: disable-msg=C6013 _ERROR_CATEGORIES = [ 'build/class', 'build/deprecated', 'build/endif_comment', 'build/explicit_make_pair', 'build/forward_decl', 'build/header_guard', 'build/include', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', 'build/namespaces', 'build/printf_format', 'build/storage_class', 'legal/copyright', 'readability/braces', 'readability/casting', 'readability/check', 'readability/constructors', 'readability/fn_size', 'readability/function', 'readability/multiline_comment', 'readability/multiline_string', 'readability/nolint', 'readability/streams', 'readability/todo', 'readability/utf8', 'runtime/arrays', 'runtime/casting', 'runtime/explicit', 'runtime/int', 'runtime/init', 'runtime/invalid_increment', 'runtime/member_string_references', 'runtime/memset', 'runtime/operator', 'runtime/printf', 'runtime/printf_format', 'runtime/references', 'runtime/rtti', 'runtime/sizeof', 'runtime/string', 'runtime/threadsafe_fn', 'runtime/virtual', 'whitespace/blank_line', 'whitespace/braces', 'whitespace/comma', 'whitespace/comments', 'whitespace/end_of_line', 'whitespace/ending_newline', 'whitespace/indent', 'whitespace/labels', 'whitespace/line_length', 'whitespace/newline', 'whitespace/operators', 'whitespace/parens', 'whitespace/semicolon', 'whitespace/tab', 'whitespace/todo' ] # The default state of the category filter. This is overrided by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. _DEFAULT_FILTERS = ['-build/include_alpha'] # We used to check for high-bit characters, but after much discussion we # decided those were OK, as long as they were in UTF-8 and didn't represent # hard-coded international strings, which belong in a separate i18n file. # Headers that we consider STL headers. _STL_HEADERS = frozenset([ 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', 'utility', 'vector', 'vector.h', ]) # Non-STL C++ system headers. _CPP_HEADERS = frozenset([ 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', ]) # Assertion macros. These are defined in base/logging.h and # testing/base/gunit.h. Note that the _M versions need to come first # for substring matching to work. _CHECK_MACROS = [ 'DCHECK', 'CHECK', 'EXPECT_TRUE_M', 'EXPECT_TRUE', 'ASSERT_TRUE_M', 'ASSERT_TRUE', 'EXPECT_FALSE_M', 'EXPECT_FALSE', 'ASSERT_FALSE_M', 'ASSERT_FALSE', ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) for op, replacement in [('==', 'EQ'), ('!=', 'NE'), ('>=', 'GE'), ('>', 'GT'), ('<=', 'LE'), ('<', 'LT')]: _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). _C_SYS_HEADER = 1 _CPP_SYS_HEADER = 2 _LIKELY_MY_HEADER = 3 _POSSIBLE_MY_HEADER = 4 _OTHER_HEADER = 5 _regexp_compile_cache = {} # Finds occurrences of NOLINT or NOLINT(...). _RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. _error_suppressions = {} if sys.version_info < (3,): def u(x): return codecs.unicode_escape_decode(x)[0] TEXT_TYPE = unicode # BINARY_TYPE = str range = xrange itervalues = dict.itervalues iteritems = dict.iteritems else: def u(x): return x TEXT_TYPE = str # BINARY_TYPE = bytes itervalues = dict.values iteritems = dict.items def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of error-suppressions. Parses any NOLINT comments on the current line, updating the global error_suppressions store. Reports an error if the NOLINT comment was malformed. Args: filename: str, the name of the input file. raw_line: str, the line of input text, with comments. linenum: int, the number of the current line. error: function, an error handler. """ # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). matched = _RE_SUPPRESSION.search(raw_line) if matched: category = matched.group(1) if category in (None, '(*)'): # => "suppress all" _error_suppressions.setdefault(None, set()).add(linenum) else: if category.startswith('(') and category.endswith(')'): category = category[1:-1] if category in _ERROR_CATEGORIES: _error_suppressions.setdefault(category, set()).add(linenum) else: error(filename, linenum, 'readability/nolint', 5, 'Unknown NOLINT error category: %s' % category) def ResetNolintSuppressions(): "Resets the set of NOLINT suppressions to empty." _error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by ParseNolintSuppressions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: bool, True iff the error should be suppressed due to a NOLINT comment. """ return (linenum in _error_suppressions.get(category, set()) or linenum in _error_suppressions.get(None, set())) def Match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" # The regexp compilation caching is inlined in both Match and Search for # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].search(s) class _IncludeState(dict): """Tracks line numbers for includes, and the order in which includes appear. As a dict, an _IncludeState object serves as a mapping between include filename and line number on which that file was included. Call CheckNextIncludeOrder() once for each header in the file, passing in the type constants defined above. Calls in an illegal order will raise an _IncludeError with an appropriate error message. """ # self._section will move monotonically through this set. If it ever # needs to move backwards, CheckNextIncludeOrder will raise an error. _INITIAL_SECTION = 0 _MY_H_SECTION = 1 _C_SECTION = 2 _CPP_SECTION = 3 _OTHER_H_SECTION = 4 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', _CPP_SYS_HEADER: 'C++ system header', _LIKELY_MY_HEADER: 'header this file implements', _POSSIBLE_MY_HEADER: 'header this file may implement', _OTHER_HEADER: 'other header', } _SECTION_NAMES = { _INITIAL_SECTION: "... nothing. (This can't be an error.)", _MY_H_SECTION: 'a header this file implements', _C_SECTION: 'C system header', _CPP_SECTION: 'C++ system header', _OTHER_H_SECTION: 'other header', } def __init__(self): dict.__init__(self) # The name of the current section. self._section = self._INITIAL_SECTION # The path of last found header. self._last_header = '' def CanonicalizeAlphabeticalOrder(self, header_path): """Returns a path canonicalized for alphabetical comparison. - replaces "-" with "_" so they both cmp the same. - removes '-inl' since we don't require them to be after the main header. - lowercase everything, just in case. Args: header_path: Path to be canonicalized. Returns: Canonicalized path. """ return header_path.replace('-inl.h', '.h').replace('-', '_').lower() def IsInAlphabeticalOrder(self, header_path): """Check if a header is in alphabetical order with the previous header. Args: header_path: Header to be checked. Returns: Returns true if the header is in alphabetical order. """ canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) if self._last_header > canonical_header: return False self._last_header = canonical_header return True def CheckNextIncludeOrder(self, header_type): """Returns a non-empty error message if the next header is out of order. This function also updates the internal state to be ready to check the next include. Args: header_type: One of the _XXX_HEADER constants defined above. Returns: The empty string if the header is in the right order, or an error message describing what's wrong. """ error_message = ('Found %s after %s' % (self._TYPE_NAMES[header_type], self._SECTION_NAMES[self._section])) last_section = self._section if header_type == _C_SYS_HEADER: if self._section <= self._C_SECTION: self._section = self._C_SECTION else: self._last_header = '' return error_message elif header_type == _CPP_SYS_HEADER: if self._section <= self._CPP_SECTION: self._section = self._CPP_SECTION else: self._last_header = '' return error_message elif header_type == _LIKELY_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: self._section = self._OTHER_H_SECTION elif header_type == _POSSIBLE_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: # This will always be the fallback because we're not sure # enough that the header is associated with this file. self._section = self._OTHER_H_SECTION else: assert header_type == _OTHER_HEADER self._section = self._OTHER_H_SECTION if last_section != self._section: self._last_header = '' return '' class _CppLintState(object): """Maintains module-wide state..""" def __init__(self): self.verbose_level = 1 # global setting. self.error_count = 0 # global count of reported errors # filters to apply when emitting error messages self.filters = _DEFAULT_FILTERS[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts # output format: # "emacs" - format that emacs can parse (default) # "vs7" - format that Microsoft Visual Studio 7 can parse self.output_format = 'emacs' def SetOutputFormat(self, output_format): """Sets the output format for errors.""" self.output_format = output_format def SetVerboseLevel(self, level): """Sets the module's verbosity, and returns the previous setting.""" last_verbose_level = self.verbose_level self.verbose_level = level return last_verbose_level def SetCountingStyle(self, counting_style): """Sets the module's counting options.""" self.counting = counting_style def SetFilters(self, filters): """Sets the error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "+whitespace/indent"). Each filter should start with + or -; else we die. Raises: ValueError: The comma-separated filters did not all start with '+' or '-'. E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" """ # Default filters always have less priority than the flag ones. self.filters = _DEFAULT_FILTERS[:] for filt in filters.split(','): clean_filt = filt.strip() if clean_filt: self.filters.append(clean_filt) for filt in self.filters: if not (filt.startswith('+') or filt.startswith('-')): raise ValueError('Every filter in --filters must start with + or -' ' (%s does not)' % filt) def ResetErrorCounts(self): """Sets the module's error statistic back to zero.""" self.error_count = 0 self.errors_by_category = {} def IncrementErrorCount(self, category): """Bumps the module's error statistic.""" self.error_count += 1 if self.counting in ('toplevel', 'detailed'): if self.counting != 'detailed': category = category.split('/')[0] if category not in self.errors_by_category: self.errors_by_category[category] = 0 self.errors_by_category[category] += 1 def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" for category, count in iteritems(self.errors_by_category): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) if self.error_count: sys.stderr.write('Total errors found: %d\n' % self.error_count) _cpplint_state = _CppLintState() def _OutputFormat(): """Gets the module's output format.""" return _cpplint_state.output_format def _SetOutputFormat(output_format): """Sets the module's output format.""" _cpplint_state.SetOutputFormat(output_format) def _VerboseLevel(): """Returns the module's verbosity setting.""" return _cpplint_state.verbose_level def _SetVerboseLevel(level): """Sets the module's verbosity, and returns the previous setting.""" return _cpplint_state.SetVerboseLevel(level) def _SetCountingStyle(level): """Sets the module's counting options.""" _cpplint_state.SetCountingStyle(level) def _Filters(): """Returns the module's list of output filters, as a list.""" return _cpplint_state.filters def _SetFilters(filters): """Sets the module's error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "whitespace/indent"). Each filter should start with + or -; else we die. """ _cpplint_state.SetFilters(filters) class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. def __init__(self): self.in_a_function = False self.lines_in_function = 0 self.current_function = '' def Begin(self, function_name): """Start analyzing function body. Args: function_name: The name of the function being tracked. """ self.in_a_function = True self.lines_in_function = 0 self.current_function = function_name def Count(self): """Count line in current function body.""" if self.in_a_function: self.lines_in_function += 1 def Check(self, error, filename, linenum): """Report if too many lines in function body. Args: error: The function to call with any errors found. filename: The name of the current file. linenum: The number of the line to check. """ if Match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: base_trigger = self._NORMAL_TRIGGER trigger = base_trigger * 2**_VerboseLevel() if self.lines_in_function > trigger: error_level = int(math.log(self.lines_in_function / base_trigger, 2)) # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... if error_level > 5: error_level = 5 error(filename, linenum, 'readability/fn_size', error_level, 'Small and focused functions are preferred:' ' %s has %d non-comment lines' ' (error triggered by exceeding %d lines).' % ( self.current_function, self.lines_in_function, trigger)) def End(self): """Stop analyzing function body.""" self.in_a_function = False class _IncludeError(Exception): """Indicates a problem with the include order in a file.""" pass class FileInfo: """Provides utility functions for filenames. FileInfo provides easy access to the components of a file's path relative to the project root. """ def __init__(self, filename): self._filename = filename def FullName(self): """Make Windows paths like Unix.""" return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): """FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from the name so that we get header guards that don't include things like "C:\Documents and Settings\..." or "/home/username/..." in them and thus people on different computers who have checked the source out to different locations won't see bogus errors. """ fullname = self.FullName() if os.path.exists(fullname): project_dir = os.path.dirname(fullname) if os.path.exists(os.path.join(project_dir, ".svn")): # If there's a .svn file in the current directory, we recursively look # up the directory tree for the top of the SVN checkout root_dir = project_dir one_up_dir = os.path.dirname(root_dir) while os.path.exists(os.path.join(one_up_dir, ".svn")): root_dir = os.path.dirname(root_dir) one_up_dir = os.path.dirname(one_up_dir) prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by # searching up from the current path. root_dir = os.path.dirname(fullname) while (root_dir != os.path.dirname(root_dir) and not os.path.exists(os.path.join(root_dir, ".git")) and not os.path.exists(os.path.join(root_dir, ".hg")) and not os.path.exists(os.path.join(root_dir, ".svn"))): root_dir = os.path.dirname(root_dir) if (os.path.exists(os.path.join(root_dir, ".git")) or os.path.exists(os.path.join(root_dir, ".hg")) or os.path.exists(os.path.join(root_dir, ".svn"))): prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Don't know what to do; header guard warnings may be wrong... return fullname def Split(self): """Splits the file into the directory, basename, and extension. For 'chrome/browser/browser.cc', Split() would return ('chrome/browser', 'browser', '.cc') Returns: A tuple of (directory, basename, extension). """ googlename = self.RepositoryName() project, rest = os.path.split(googlename) return (project,) + os.path.splitext(rest) def BaseName(self): """File base name - text after the final slash, before the final period.""" return self.Split()[1] def Extension(self): """File extension - text following the final period.""" return self.Split()[2] def NoExtension(self): """File has no source file extension.""" return '/'.join(self.Split()[0:2]) def IsSource(self): """File has a source file extension.""" return self.Extension()[1:] in EXTENSIONS def _ShouldPrintError(category, confidence, linenum): """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: # a "NOLINT(category)" comment appears in the source, # the verbosity level isn't high enough, or the filters filter it out. if IsErrorSuppressedByNolint(category, linenum): return False if confidence < _cpplint_state.verbose_level: return False is_filtered = False for one_filter in _Filters(): if one_filter.startswith('-'): if category.startswith(one_filter[1:]): is_filtered = True elif one_filter.startswith('+'): if category.startswith(one_filter[1:]): is_filtered = False else: assert False # should have been checked for in SetFilter. if is_filtered: return False return True def Error(filename, linenum, category, confidence, message): """Logs the fact we've found a lint error. We log where the error was found, and also our confidence in the error, that is, how certain we are this is a legitimate style regression, and not a misidentification or a use that's sometimes justified. False positives can be suppressed by the use of "cpplint(category)" comments on the offending line. These are parsed into _error_suppressions. Args: filename: The name of the file containing the error. linenum: The number of the line containing the error. category: A string used to describe the "category" this bug falls under: "whitespace", say, or "runtime". Categories may have a hierarchy separated by slashes: "whitespace/indent". confidence: A number from 1-5 representing a confidence score for the error, with 5 meaning that we are certain of the problem, and 1 meaning that it could be a legitimate construct. message: The error message. """ if _ShouldPrintError(category, confidence, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) else: m = '%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence) sys.stderr.write(m) # Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') # Matches strings. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') # Matches characters. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") # Matches multi-line C++ comments. # This RE is a little bit more complicated than one might expect, because we # have to take care of space removals tools so we can handle comments inside # statements better. # The current rule is: We only clear spaces from both sides when we're at the # end of the line. Otherwise, we try to remove spaces from the right side, # if this doesn't work we try on left side but only if there's a non-character # on the right. _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( r"""(\s*/\*.*\*/\s*$| /\*.*\*/\s+| \s+/\*.*\*/(?=\W)| /\*.*\*/)""", re.VERBOSE) def IsCppString(line): """Does line terminate so, that the next symbol is in string constant. This function does not consider single-line nor multi-line comments. Args: line: is a partial line of code starting from the 0..n. Returns: True, if next character appended to 'line' is inside a string constant. """ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 def FindNextMultiLineCommentStart(lines, lineix): """Find the beginning marker for a multiline comment.""" while lineix < len(lines): if lines[lineix].strip().startswith('/*'): # Only return this marker if the comment goes beyond this line if lines[lineix].strip().find('*/', 2) < 0: return lineix lineix += 1 return len(lines) def FindNextMultiLineCommentEnd(lines, lineix): """We are inside a comment, find the end marker.""" while lineix < len(lines): if lines[lineix].strip().endswith('*/'): return lineix lineix += 1 return len(lines) def RemoveMultiLineCommentsFromRange(lines, begin, end): """Clears a range of lines for multi-line comments.""" # Having // dummy comments makes the lines non-empty, so we will not get # unnecessary blank line warnings later in the code. for i in range(begin, end): lines[i] = '// dummy' def RemoveMultiLineComments(filename, lines, error): """Removes multiline (c-style) comments from lines.""" lineix = 0 while lineix < len(lines): lineix_begin = FindNextMultiLineCommentStart(lines, lineix) if lineix_begin >= len(lines): return lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) if lineix_end >= len(lines): error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, 'Could not find end of multi-line comment') return RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) lineix = lineix_end + 1 def CleanseComments(line): """Removes //-comments and single-line C-style /* */ comments. Args: line: A line of C++ source. Returns: The line with single-line comments removed. """ commentpos = line.find('//') if commentpos != -1 and not IsCppString(line[:commentpos]): line = line[:commentpos].rstrip() # get rid of /* ... */ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) class CleansedLines(object): """Holds 3 copies of all lines with different preprocessing applied to them. 1) elided member contains lines without strings and comments, 2) lines member contains lines without comments, and 3) raw member contains all the lines without processing. All these three members are of , and of the same length. """ def __init__(self, lines): self.elided = [] self.lines = [] self.raw_lines = lines self.num_lines = len(lines) for linenum in range(len(lines)): self.lines.append(CleanseComments(lines[linenum])) elided = self._CollapseStrings(lines[linenum]) self.elided.append(CleanseComments(elided)) def NumLines(self): """Returns the number of lines represented.""" return self.num_lines @staticmethod def _CollapseStrings(elided): """Collapses strings and chars on a line to simple "" or '' blocks. We nix strings first so we're not fooled by text like '"http://"' Args: elided: The line being processed. Returns: The line with collapsed strings. """ if not _RE_PATTERN_INCLUDE.match(elided): # Remove escaped characters first to make quote/single quote collapsing # basic. Things that look like escaped characters shouldn't occur # outside of strings and chars. elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) return elided def CloseExpression(clean_lines, linenum, pos): """If input points to ( or { or [, finds the position that closes it. If lines[linenum][pos] points to a '(' or '{' or '[', finds the linenum/pos that correspond to the closing of the expression. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: A position on the line. Returns: A tuple (line, linenum, pos) pointer *past* the closing brace, or (line, len(lines), -1) if we never find a close. Note we ignore strings and comments when matching; and the line we return is the 'cleansed' line at linenum. """ line = clean_lines.elided[linenum] startchar = line[pos] if startchar not in '({[': return (line, clean_lines.NumLines(), -1) if startchar == '(': endchar = ')' if startchar == '[': endchar = ']' if startchar == '{': endchar = '}' num_open = line.count(startchar) - line.count(endchar) while linenum < clean_lines.NumLines() and num_open > 0: linenum += 1 line = clean_lines.elided[linenum] num_open += line.count(startchar) - line.count(endchar) # OK, now find the endchar that actually got us back to even endpos = len(line) while num_open >= 0: endpos = line.rfind(')', 0, endpos) num_open -= 1 # chopped off another ) return (line, linenum, endpos + 1) def CheckForCopyright(filename, lines, error): """Logs an error if no Copyright message appears at the top of the file.""" # We'll say it should occur by line 10. Don't forget there's a # dummy line at the front. for line in range(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, 'No copyright message found. ' 'You should have a line: "Copyright [year] "') def GetHeaderGuardCPPVariable(filename): """Returns the CPP variable that should be used as a header guard. Args: filename: The name of a C++ header file. Returns: The CPP variable that should be used as a header guard in the named file. """ # Restores original filename in case that cpplint is invoked from Emacs's # flymake. filename = re.sub(r'_flymake\.h$', '.h', filename) fileinfo = FileInfo(filename) return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_' def CheckForHeaderGuard(filename, lines, error): """Checks that the file contains a header guard. Logs an error if no #ifndef header guard is present. For other headers, checks that the full pathname is used. Args: filename: The name of the C++ header file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ cppvar = GetHeaderGuardCPPVariable(filename) ifndef = None ifndef_linenum = 0 define = None endif = None endif_linenum = 0 for linenum, line in enumerate(lines): linesplit = line.split() if len(linesplit) >= 2: # find the first occurrence of #ifndef and #define, save arg if not ifndef and linesplit[0] == '#ifndef': # set ifndef to the header guard presented on the #ifndef line. ifndef = linesplit[1] ifndef_linenum = linenum if not define and linesplit[0] == '#define': define = linesplit[1] # find the last occurrence of #endif, save entire line if line.startswith('#endif'): endif = line endif_linenum = linenum if not ifndef: error(filename, 0, 'build/header_guard', 5, 'No #ifndef header guard found, suggested CPP variable is: %s' % cppvar) return if not define: error(filename, 0, 'build/header_guard', 5, 'No #define header guard found, suggested CPP variable is: %s' % cppvar) return # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ # for backward compatibility. if ifndef != cppvar: error_level = 0 if ifndef != cppvar + '_': error_level = 5 ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, '#ifndef header guard has wrong style, please use: %s' % cppvar) if define != ifndef: error(filename, 0, 'build/header_guard', 5, '#ifndef and #define don\'t match, suggested CPP variable is: %s' % cppvar) return if endif != ('#endif // %s' % cppvar): error_level = 0 if endif != ('#endif // %s' % (cppvar + '_')): error_level = 5 ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, error) error(filename, endif_linenum, 'build/header_guard', error_level, '#endif line should be "#endif // %s"' % cppvar) def CheckForUnicodeReplacementCharacters(filename, lines, error): """Logs an error for each line containing Unicode replacement characters. These indicate that either the file contained invalid UTF-8 (likely) or Unicode replacement characters (which it shouldn't). Note that it's possible for this to throw off line numbering if the invalid UTF-8 occurred adjacent to a newline. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ for linenum, line in enumerate(lines): if u('\ufffd') in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') def CheckForNewlineAtEOF(filename, lines, error): """Logs an error if there is no newline char at the end of the file. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ # The array lines() was created by adding two newlines to the # original file (go figure), then splitting on \n. # To verify that the file ends in \n, we just have to make sure the # last-but-two element of lines() exists and is empty. if len(lines) < 3 or lines[-2]: error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, 'Could not find a newline character at the end of the file.') def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): """Logs an error if we see /* ... */ or "..." that extend past one line. /* ... */ comments are legit inside macros, for one line. Otherwise, we prefer // comments, so it's ok to warn about the other. Likewise, it's ok for strings to extend across multiple lines, as long as a line continuation character (backslash) terminates each line. Although not currently prohibited by the C++ style guide, it's ugly and unnecessary. We don't do well with either in this lint program, so we warn about both. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Remove all \\ (escaped backslashes) from the line. They are OK, and the # second (escaped) slash may trigger later \" detection erroneously. line = line.replace('\\\\', '') if line.count('/*') > line.count('*/'): error(filename, linenum, 'readability/multiline_comment', 5, 'Complex multi-line /*...*/-style comment found. ' 'Lint may give bogus warnings. ' 'Consider replacing these with //-style comments, ' 'with #if 0...#endif, ' 'or with more clearly structured multi-line comments.') if (line.count('"') - line.count('\\"')) % 2: error(filename, linenum, 'readability/multiline_string', 5, 'Multi-line string ("...") found. This lint script doesn\'t ' 'do well with such strings, and may give bogus warnings. They\'re ' 'ugly and unnecessary, and you should use concatenation instead".') threading_list = ( ('asctime(', 'asctime_r('), ('ctime(', 'ctime_r('), ('getgrgid(', 'getgrgid_r('), ('getgrnam(', 'getgrnam_r('), ('getlogin(', 'getlogin_r('), ('getpwnam(', 'getpwnam_r('), ('getpwuid(', 'getpwuid_r('), ('gmtime(', 'gmtime_r('), ('localtime(', 'localtime_r('), ('rand(', 'rand_r('), ('readdir(', 'readdir_r('), ('strtok(', 'strtok_r('), ('ttyname(', 'ttyname_r('), ) def CheckPosixThreading(filename, clean_lines, linenum, error): """Checks for calls to thread-unsafe functions. Much code has been originally written without consideration of multi-threading. Also, engineers are relying on their old experience; they have learned posix before threading extensions were added. These tests guide the engineers to use thread-safe functions (when using posix directly). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] for single_thread_function, multithread_safe_function in threading_list: ix = line.find(single_thread_function) # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and line[ix - 1] not in ('_', '.', '>'))): error(filename, linenum, 'runtime/threadsafe_fn', 2, 'Consider using ' + multithread_safe_function + '...) instead of ' + single_thread_function + '...) for improved thread safety.') # Matches invalid increment: *count++, which moves pointer instead of # incrementing a value. _RE_PATTERN_INVALID_INCREMENT = re.compile( r'^\s*\*\w+(\+\+|--);') def CheckInvalidIncrement(filename, clean_lines, linenum, error): """Checks for invalid increment *count++. For example following function: void increment_counter(int* count) { *count++; } is invalid, because it effectively does count++, moving pointer, and should be replaced with ++*count, (*count)++ or *count += 1. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] if _RE_PATTERN_INVALID_INCREMENT.match(line): error(filename, linenum, 'runtime/invalid_increment', 5, 'Changing pointer instead of value (or unused value of operator*).') class _ClassInfo(object): """Stores information about a class.""" def __init__(self, name, clean_lines, linenum): self.name = name self.linenum = linenum self.seen_open_brace = False self.is_derived = False self.virtual_method_linenumber = None self.has_virtual_destructor = False self.brace_depth = 0 # Try to find the end of the class. This will be confused by things like: # class A { # } *x = { ... # # But it's still good enough for CheckSectionSpacing. self.last_line = 0 depth = 0 for i in range(linenum, clean_lines.NumLines()): line = clean_lines.lines[i] depth += line.count('{') - line.count('}') if not depth: self.last_line = i break class _ClassState(object): """Holds the current state of the parse relating to class declarations. It maintains a stack of _ClassInfos representing the parser's guess as to the current nesting of class declarations. The innermost class is at the top (back) of the stack. Typically, the stack will either be empty or have exactly one entry. """ def __init__(self): self.classinfo_stack = [] def CheckFinished(self, filename, error): """Checks that all classes have been completely parsed. Call this when all lines in a file have been processed. Args: filename: The name of the current file. error: The function to call with any errors found. """ if self.classinfo_stack: # Note: This test can result in false positives if #ifdef constructs # get in the way of brace matching. See the testBuildClass test in # cpplint_unittest.py for an example of this. error(filename, self.classinfo_stack[0].linenum, 'build/class', 5, 'Failed to find complete declaration of class %s' % self.classinfo_stack[0].name) def CheckForNonStandardConstructs(filename, clean_lines, linenum, class_state, error): """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. Complain about several constructs which gcc-2 accepts, but which are not standard C++. Warning about these in lint is one way to ease the transition to new compilers. - put storage class first (e.g. "static const" instead of "const static"). - "%lld" instead of %qd" in printf-type functions. - "%1$d" is non-standard in printf-type functions. - "\%" is an undefined character escape sequence. - text after #endif is not allowed. - invalid inner-style forward declaration. - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line): error(filename, linenum, 'build/deprecated', 3, '>? and ))?' # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' error(filename, linenum, 'runtime/member_string_references', 2, 'const string& members are dangerous. It is much better to use ' 'alternatives, such as pointers or simple constants.') # Track class entry and exit, and attempt to find cases within the # class declaration that don't meet the C++ style # guidelines. Tracking is very dependent on the code matching Google # style guidelines, but it seems to perform well enough in testing # to be a worthwhile addition to the checks. classinfo_stack = class_state.classinfo_stack # Look for a class declaration. The regexp accounts for decorated classes # such as in: # class LOCKABLE API Object { # }; class_decl_match = Match( r'\s*(template\s*<[\w\s<>,:]*>\s*)?' '(class|struct)\s+([A-Z_]+\s+)*(\w+(::\w+)*)', line) if class_decl_match: classinfo_stack.append(_ClassInfo( class_decl_match.group(4), clean_lines, linenum)) # Everything else in this function uses the top of the stack if it's # not empty. if not classinfo_stack: return classinfo = classinfo_stack[-1] # If the opening brace hasn't been seen look for it and also # parent class declarations. if not classinfo.seen_open_brace: # If the line has a ';' in it, assume it's a forward declaration or # a single-line class declaration, which we won't process. if line.find(';') != -1: classinfo_stack.pop() return classinfo.seen_open_brace = (line.find('{') != -1) # Look for a bare ':' if Search('(^|[^:]):($|[^:])', line): classinfo.is_derived = True if not classinfo.seen_open_brace: return # Everything else in this function is for after open brace # The class may have been declared with namespace or classname qualifiers. # The constructor and destructor will not have those qualifiers. base_classname = classinfo.name.split('::')[-1] # Look for single-argument constructors that aren't marked explicit. # Technically a valid construct, but against style. args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' % re.escape(base_classname), line) if (args and args.group(1) != 'void' and not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), args.group(1).strip())): error(filename, linenum, 'runtime/explicit', 5, 'Single-argument constructors should be marked explicit.') # Look for methods declared virtual. if Search(r'\bvirtual\b', line): classinfo.virtual_method_linenumber = linenum # Only look for a destructor declaration on the same line. It would # be extremely unlikely for the destructor declaration to occupy # more than one line. if Search(r'~%s\s*\(' % base_classname, line): classinfo.has_virtual_destructor = True # Look for class end. brace_depth = classinfo.brace_depth brace_depth = brace_depth + line.count('{') - line.count('}') if brace_depth <= 0: classinfo = classinfo_stack.pop() # Try to detect missing virtual destructor declarations. # For now, only warn if a non-derived class with virtual methods lacks # a virtual destructor. This is to make it less likely that people will # declare derived virtual destructors without declaring the base # destructor virtual. if ((classinfo.virtual_method_linenumber is not None) and (not classinfo.has_virtual_destructor) and (not classinfo.is_derived)): # Only warn for base classes error(filename, classinfo.linenum, 'runtime/virtual', 4, 'The class %s probably needs a virtual destructor due to ' 'having virtual method(s), one declared at line %d.' % (classinfo.name, classinfo.virtual_method_linenumber)) else: classinfo.brace_depth = brace_depth def CheckSpacingForFunctionCall(filename, line, linenum, error): """Checks for the correctness of various spacing around function calls. Args: filename: The name of the current file. line: The text of the line to check. linenum: The number of the line to check. error: The function to call with any errors found. """ # Since function calls often occur inside if/for/while/switch # expressions - which have their own, more liberal conventions - we # first see if we should be looking inside such an expression for a # function call, to which we can apply more strict standards. fncall = line # if there's no control flow construct, look at whole line for pattern in (r'\bif\s*\((.*)\)\s*{', r'\bfor\s*\((.*)\)\s*{', r'\bwhile\s*\((.*)\)\s*[{;]', r'\bswitch\s*\((.*)\)\s*{'): match = Search(pattern, line) if match: fncall = match.group(1) # look inside the parens for function calls break # Except in if/for/while/switch, there should never be space # immediately inside parens (eg "f( 3, 4 )"). We make an exception # for nested parens ( (a+b) + c ). Likewise, there should never be # a space before a ( when it's a function argument. I assume it's a # function argument when the char before the whitespace is legal in # a function name (alnum + _) and we're not starting a macro. Also ignore # pointers and references to arrays and functions coz they're too tricky: # we use a very simple way to recognize these: # " (something)(maybe-something)" or # " (something)(maybe-something," or # " (something)[something]" # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and # Ignore pointers/references to functions. not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call error(filename, linenum, 'whitespace/parens', 4, 'Extra space after ( in function call') elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') if (Search(r'\w\s+\(', fncall) and not Search(r'#\s*define|typedef', fncall)): error(filename, linenum, 'whitespace/parens', 4, 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain if Search(r'[^)]\s+\)\s*[^{\s]', fncall): # If the closing parenthesis is preceded by only whitespaces, # try to give a more descriptive error message. if Search(r'^\s+\)', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Closing ) should be moved to the previous line') else: error(filename, linenum, 'whitespace/parens', 2, 'Extra space before )') def IsBlankLine(line): """Returns true if the given line is blank. We consider a line to be blank if the line is empty or consists of only white spaces. Args: line: A line of a string. Returns: True, if the given line is blank. """ return not line or line.isspace() def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error): """Reports for long function bodies. For an overview why this is done, see: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions Uses a simplistic algorithm assuming other style guidelines (especially spacing) are followed. Only checks unindented functions, so class members are unchecked. Trivial bodies are unchecked, so constructors with huge initializer lists may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal of vertical space and comments just to get through a lint check. NOLINT *on the last line of a function* disables this check. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. function_state: Current function name and lines in body so far. error: The function to call with any errors found. """ lines = clean_lines.lines line = lines[linenum] raw = clean_lines.raw_lines raw_line = raw[linenum] joined_line = '' starting_func = False regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... match_result = Match(regexp, line) if match_result: # If the name is all caps and underscores, figure it's a macro and # ignore it, unless it's TEST or TEST_F. function_name = match_result.group(1).split()[-1] if function_name == 'TEST' or function_name == 'TEST_F' or ( not Match(r'[A-Z_]+$', function_name)): starting_func = True if starting_func: body_found = False for start_linenum in range(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() if Search(r'(;|})', start_line): # Declarations and trivial functions body_found = True break # ... ignore elif Search(r'{', start_line): body_found = True function = Search(r'((\w|:)*)\(', line).group(1) if Match(r'TEST', function): # Handle TEST... macros parameter_regexp = Search(r'(\(.*\))', joined_line) if parameter_regexp: # Ignore bad syntax function += parameter_regexp.group(1) else: function += '()' function_state.Begin(function) break if not body_found: # No body for the function (or evidence of a non-function) was found. error(filename, linenum, 'readability/fn_size', 5, 'Lint failed to find start of function body.') elif Match(r'^\}\s*$', line): # function end function_state.Check(error, filename, linenum) function_state.End() elif not Match(r'^\s*$', line): function_state.Count() # Count non-blank/non-comment lines. _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') def CheckComment(comment, filename, linenum, error): """Checks for common mistakes in TODO comments. Args: comment: The text of the comment from the line in question. filename: The name of the current file. linenum: The number of the line to check. error: The function to call with any errors found. """ match = _RE_PATTERN_TODO.match(comment) if match: # One whitespace is correct; zero whitespace is handled elsewhere. leading_whitespace = match.group(1) if len(leading_whitespace) > 1: error(filename, linenum, 'whitespace/todo', 2, 'Too many spaces before TODO') username = match.group(2) if not username: error(filename, linenum, 'readability/todo', 2, 'Missing username in TODO; it should look like ' '"// TODO(my_username): Stuff."') middle_whitespace = match.group(3) # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 if middle_whitespace != ' ' and middle_whitespace != '': error(filename, linenum, 'whitespace/todo', 2, 'TODO(my_username) should be followed by a space') def CheckSpacing(filename, clean_lines, linenum, error): """Checks for the correctness of various spacing issues in the code. Things we check for: spaces around operators, spaces after if/for/while/switch, no spaces around parens in function calls, two spaces between code and comment, don't start a block with a blank line, don't end a function with a blank line, don't add a blank line after public/protected/private, don't have too many blank lines in a row. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] # Before nixing comments, check if the line is blank for no good # reason. This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' if IsBlankLine(line): elided = clean_lines.elided prev_line = elided[linenum - 1] prevbrace = prev_line.rfind('{') # TODO(unknown): Don't complain if line before blank line, and line after, # both start with alnums and are indented the same amount. # This ignores whitespace at the start of a namespace block # because those are not usually indented. if (prevbrace != -1 and prev_line[prevbrace:].find('}') == -1 and prev_line[:prevbrace].find('namespace') == -1): # OK, we have a blank line at the start of a code block. Before we # complain, we check if it is an exception to the rule: The previous # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name). We also check for the case where # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False if Match(r' {6}\w', prev_line): # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum-2 while (search_position >= 0 and Match(r' {6}\w', elided[search_position])): search_position -= 1 exception = (search_position >= 0 and elided[search_position][:5] == ' :') else: # Search for the function arguments or an initializer list. We use a # simple heuristic here: If the line is indented 4 spaces; and we have a # closing paren, without the opening paren, followed by an opening brace # or colon (for initializer lists) we assume that it is the last line of # a function header. If we have a colon indented 4 spaces, it is an # initializer list. exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', prev_line) or Match(r' {4}:', prev_line)) if not exception: error(filename, linenum, 'whitespace/blank_line', 2, 'Blank line at the start of a code block. Is this needed?') # This doesn't ignore whitespace at the end of a namespace block # because that is too hard without pairing open/close braces; # however, a special exception is made for namespace closing # brackets which have a comment containing "namespace". # # Also, ignore blank lines at the end of a block in a long if-else # chain, like this: # if (condition1) { # // Something followed by a blank line # # } else if (condition2) { # // Something else # } if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] if (next_line and Match(r'\s*}', next_line) and next_line.find('namespace') == -1 and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, 'Blank line at the end of a code block. Is this needed?') matched = Match(r'\s*(public|protected|private):', prev_line) if matched: error(filename, linenum, 'whitespace/blank_line', 3, 'Do not leave a blank line after "%s:"' % matched.group(1)) # Next, we complain if there's a comment too near the text commentpos = line.find('//') if commentpos != -1: # Check if the // may be in quotes. If so, ignore it # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if (line.count('"', 0, commentpos) - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes # Allow one space for new scopes, two spaces otherwise: if (not Match(r'^\s*{ //', line) and ((commentpos >= 1 and line[commentpos-1] not in string.whitespace) or (commentpos >= 2 and line[commentpos-2] not in string.whitespace))): error(filename, linenum, 'whitespace/comments', 2, 'At least two spaces is best between code and comments') # There should always be a space between the // and the comment commentend = commentpos + 2 if commentend < len(line) and not line[commentend] == ' ': # but some lines are exceptions -- e.g. if they're big # comment delimiters like: # //---------------------------------------------------------- # or are an empty C++ style Doxygen comment, like: # /// # or they begin with multiple slashes followed by a space: # //////// Header comment match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^/+ ', line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') CheckComment(line[commentpos:], filename, linenum, error) line = clean_lines.elided[linenum] # get rid of comments and strings # Don't try to do spacing checks for operator methods line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. # You should always have whitespace around binary operators. # Alas, we can't test < or > because they're legitimately used sans spaces # (a->b, vector a). The only time we can tell is a < with no >, and # only if it's not template params list spilling into the next line. match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) if not match: # Note that while it seems that the '<[^<]*' term in the following # regexp could be simplified to '<.*', which would indeed match # the same class of strings, the [^<] means that searching for the # regexp takes linear rather than quadratic time. if not Search(r'<[^<]*,\s*$', line): # template params spill match = Search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) # We allow no-spaces around << and >> when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) match = Search(r'[^0-9\s](<<|>>)[^0-9\s]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: error(filename, linenum, 'whitespace/operators', 4, 'Extra space for operator %s' % match.group(1)) # A pet peeve of mine: no spaces after an if, while, switch, or for match = Search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, 'Missing space before ( in %s' % match.group(1)) # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. match = Search(r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', line) if match: if len(match.group(2)) != len(match.group(4)): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, 'Mismatching spaces inside () in %s' % match.group(1)) if not len(match.group(2)) in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, 'Should have zero or one spaces inside ( and ) in %s' % match.group(1)) # You should always have a space after a comma (either as fn arg or operator) if Search(r',[^\s]', line): error(filename, linenum, 'whitespace/comma', 3, 'Missing space after ,') # You should always have a space after a semicolon # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; if Search(r';[^\s};\\)/]', line): error(filename, linenum, 'whitespace/semicolon', 3, 'Missing space after ;') # Next we will look for issues with function calls. CheckSpacingForFunctionCall(filename, line, linenum, error) # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. if Search(r'[^ ({]{', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before {') # Make sure '} else {' has spaces. if Search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'new char * []'. if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): error(filename, linenum, 'whitespace/braces', 5, 'Extra space before [') # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Semicolon defining empty statement. Use { } instead.') elif Search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' 'use { } instead.') elif (Search(r'\s+;\s*$', line) and not Search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' 'statement, use { } instead.') def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): """Checks for additional blank line issues related to sections. Currently the only thing checked here is blank line before protected/private. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. class_info: A _ClassInfo objects. linenum: The number of the line to check. error: The function to call with any errors found. """ # Skip checks if the class is small, where small means 25 lines or less. # 25 lines seems like a good cutoff since that's the usual height of # terminals, and any class that can't fit in one screen can't really # be considered "small". # # Also skip checks if we are on the first line. This accounts for # classes that look like # class Foo { public: ... }; # # If we didn't find the end of the class, last_line would be zero, # and the check will be skipped by the first condition. if (class_info.last_line - class_info.linenum <= 24 or linenum <= class_info.linenum): return matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) if matched: # Issue warning if the line before public/protected/private was # not a blank line, but don't do this if the previous line contains # "class" or "struct". This can happen two ways: # - We are at the beginning of the class. # - We are forward-declaring an inner class that is semantically # private, but needed to be public for implementation reasons. prev_line = clean_lines.lines[linenum - 1] if (not IsBlankLine(prev_line) and not Search(r'\b(class|struct)\b', prev_line)): # Try a bit harder to find the beginning of the class. This is to # account for multi-line base-specifier lists, e.g.: # class Derived # : public Base { end_class_head = class_info.linenum for i in range(class_info.linenum, linenum): if Search(r'\{\s*$', clean_lines.lines[i]): end_class_head = i break if end_class_head < linenum - 1: error(filename, linenum, 'whitespace/blank_line', 3, '"%s:" should be preceded by a blank line' % matched.group(1)) def GetPreviousNonBlankLine(clean_lines, linenum): """Return the most recent non-blank line and its line number. Args: clean_lines: A CleansedLines instance containing the file contents. linenum: The number of the line to check. Returns: A tuple with two elements. The first element is the contents of the last non-blank line before the current line, or the empty string if this is the first non-blank line. The second is the line number of that line, or -1 if this is the first non-blank line. """ prevlinenum = linenum - 1 while prevlinenum >= 0: prevline = clean_lines.elided[prevlinenum] if not IsBlankLine(prevline): # if not a blank line... return (prevline, prevlinenum) prevlinenum -= 1 return ('', -1) def CheckBraces(filename, clean_lines, linenum, error): """Looks for misplaced braces (e.g. at the end of line). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings if Match(r'\s*{\s*$', line): # We allow an open brace to start a line in the case where someone # is using braces in a block to explicitly create a new scope, # which is commonly used to control the lifetime of # stack-allocated variables. We don't detect this perfectly: we # just don't complain if the last non-whitespace character on the # previous non-blank line is ';', ':', '{', or '}'. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if not Search(r'[;:}{]\s*$', prevline): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. if Match(r'\s*else\s*', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if Match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, 'An else should appear on the same line as the preceding }') # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if # find the ( after the if pos = line.find('else if') pos = line.find('(', pos) if pos > 0: (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) if endline[endpos:].find('{') == -1: # must be brace after if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') else: # common case: else not followed by a multi-line if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): error(filename, linenum, 'whitespace/newline', 4, 'Else clause should never be on same line as else (use 2 lines)') # In the same way, a do/while should never be on one line if Match(r'\s*do [^\s{]', line): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') # Braces shouldn't be followed by a ; unless they're defining a struct # or initializing an array. # We can't tell in general, but we can for some common cases. prevlinenum = linenum while True: (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): line = prevline + line else: break if (Search(r'{.*}\s*;', line) and line.count('{') == line.count('}') and not Search(r'struct|class|enum|\s*=\s*{', line)): error(filename, linenum, 'readability/braces', 4, "You don't need a ; after a }") def ReplaceableCheck(operator, macro, line): """Determine whether a basic CHECK can be replaced with a more specific one. For example suggest using CHECK_EQ instead of CHECK(a == b) and similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. Args: operator: The C++ operator used in the CHECK. macro: The CHECK or EXPECT macro being called. line: The current source line. Returns: True if the CHECK can be replaced with a more specific one. """ # This matches decimal and hex integers, strings, and chars (in that order). match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' # Expression to match two sides of the operator with something that # looks like a literal, since CHECK(x == iterator) won't compile. # This means we can't catch all the cases where a more specific # CHECK is possible, but it's less annoying than dealing with # extraneous warnings. match_this = (r'\s*' + macro + r'\((\s*' + match_constant + r'\s*' + operator + r'[^<>].*|' r'.*[^<>]' + operator + r'\s*' + match_constant + r'\s*\))') # Don't complain about CHECK(x == NULL) or similar because # CHECK_EQ(x, NULL) won't compile (requires a cast). # Also, don't complain about more complex boolean expressions # involving && or || such as CHECK(a == b || c == d). return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) def CheckCheck(filename, clean_lines, linenum, error): """Checks the use of CHECK and EXPECT macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Decide the set of replacement macros that should be suggested raw_lines = clean_lines.raw_lines current_macro = '' for macro in _CHECK_MACROS: if raw_lines[linenum].find(macro) >= 0: current_macro = macro break if not current_macro: # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' return line = clean_lines.elided[linenum] # get rid of comments and strings # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. for operator in ['==', '!=', '>=', '>', '<=', '<']: if ReplaceableCheck(operator, current_macro, line): error(filename, linenum, 'readability/check', 2, 'Consider using %s instead of %s(a %s b)' % ( _CHECK_REPLACEMENT[current_macro][operator], current_macro, operator)) break def GetLineWidth(line): """Determines the width of the line in column positions. Args: line: A string, which may be a Unicode string. Returns: The width of the line in column positions, accounting for Unicode combining characters and wide characters. """ if isinstance(line, TEXT_TYPE): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 elif not unicodedata.combining(uc): width += 1 return width else: return len(line) def CheckStyle(filename, clean_lines, linenum, file_extension, class_state, error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we do what we can. In particular we check for 2-space indents, line lengths, tab usage, spaces inside code, etc. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. error: The function to call with any errors found. """ raw_lines = clean_lines.raw_lines line = raw_lines[linenum] if line.find('\t') != -1: error(filename, linenum, 'whitespace/tab', 1, 'Tab found; better to use spaces') # One or three blank spaces at the beginning of the line is weird; it's # hard to reconcile that with 2-space indents. # NOTE: here are the conditions rob pike used for his tests. Mine aren't # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces # if(RLENGTH > 20) complain = 0; # if(match($0, " +(error|private|public|protected):")) complain = 0; # if(match(prev, "&& *$")) complain = 0; # if(match(prev, "\\|\\| *$")) complain = 0; # if(match(prev, "[\",=><] *$")) complain = 0; # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 if line and line[-1].isspace(): error(filename, linenum, 'whitespace/end_of_line', 4, 'Line ends in whitespace. Consider deleting these extra spaces.') # There are certain situations we allow one space, notably for labels elif ((initial_spaces == 1 or initial_spaces == 3) and not Match(r'\s*\w+\s*:\s*$', cleansed_line)): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') # Labels should always be indented at least one space. elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', line): error(filename, linenum, 'whitespace/labels', 4, 'Labels should always be indented at least one space. ' 'If this is a member-initializer list in a constructor or ' 'the base class list in a class definition, the colon should ' 'be on the following line.') # Check if the line is a header guard. is_header_guard = False if file_extension == 'h': cppvar = GetHeaderGuardCPPVariable(filename) if (line.startswith('#ifndef %s' % cppvar) or line.startswith('#define %s' % cppvar) or line.startswith('#endif // %s' % cppvar)): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. # # URLs can be long too. It's possible to split these, but it makes them # harder to cut&paste. # # The "$Id:...$" comment may also get very long without it being the # developers fault. if (not line.startswith('#include') and not is_header_guard and not Match(r'^\s*//.*http(s?)://\S*$', line) and not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) if line_width > 100: error(filename, linenum, 'whitespace/line_length', 4, 'Lines should very rarely be longer than 100 characters') elif line_width > 80: error(filename, linenum, 'whitespace/line_length', 2, 'Lines should be <= 80 characters long') if (cleansed_line.count(';') > 1 and # for loops are allowed two ;'s (and may run over two lines). cleansed_line.find('for') == -1 and (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and # It's ok to have many commands in a switch case that fits in 1 line not ((cleansed_line.find('case ') != -1 or cleansed_line.find('default:') != -1) and cleansed_line.find('break;') != -1)): error(filename, linenum, 'whitespace/newline', 4, 'More than one command on the same line') # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckSpacing(filename, clean_lines, linenum, error) CheckCheck(filename, clean_lines, linenum, error) if class_state and class_state.classinfo_stack: CheckSectionSpacing(filename, clean_lines, class_state.classinfo_stack[-1], linenum, error) _RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') def _DropCommonSuffixes(filename): """Drops common suffixes like _test.cc or -inl.h from filename. For example: >>> _DropCommonSuffixes('foo/foo-inl.h') 'foo/foo' >>> _DropCommonSuffixes('foo/bar/foo.cc') 'foo/bar/foo' >>> _DropCommonSuffixes('foo/foo_internal.h') 'foo/foo' >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') 'foo/foo_unusualinternal' Args: filename: The input filename. Returns: The filename with the common suffix removed. """ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', 'inl.h', 'impl.h', 'internal.h'): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] return os.path.splitext(filename)[0] def _IsTestFilename(filename): """Determines if the given filename has a suffix that identifies it as a test. Args: filename: The input filename. Returns: True if 'filename' looks like a test, False otherwise. """ if (filename.endswith('_test.cc') or filename.endswith('_unittest.cc') or filename.endswith('_regtest.cc')): return True else: return False def _ClassifyInclude(fileinfo, include, is_system): """Figures out what kind of header 'include' is. Args: fileinfo: The current file cpplint is running over. A FileInfo instance. include: The path to a #included file. is_system: True if the #include used <> rather than "". Returns: One of the _XXX_HEADER constants. For example: >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) _C_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) _CPP_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) _LIKELY_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), ... 'bar/foo_other_ext.h', False) _POSSIBLE_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) _OTHER_HEADER """ # This is a list of all standard c++ header files, except # those already checked for above. is_stl_h = include in _STL_HEADERS is_cpp_h = is_stl_h or include in _CPP_HEADERS if is_system: if is_cpp_h: return _CPP_SYS_HEADER else: return _C_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include # lives in . , then it's likely to be owned by the target file. target_dir, target_base = ( os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) if target_base == include_base and ( include_dir == target_dir or include_dir == os.path.normpath(target_dir + '/../public')): return _LIKELY_MY_HEADER # If the target and include share some initial basename # component, it's possible the target is implementing the # include, so it's allowed to be first, but we'll never # complain if it's not there. target_first_component = _RE_FIRST_COMPONENT.match(target_base) include_first_component = _RE_FIRST_COMPONENT.match(include_base) if (target_first_component and include_first_component and target_first_component.group(0) == include_first_component.group(0)): return _POSSIBLE_MY_HEADER return _OTHER_HEADER def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): """Check rules that are applicable to #include lines. Strings on #include lines are NOT removed from elided line, to make certain tasks easier. However, to prevent false positives, checks applicable to #include lines in CheckLanguage must be put here. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ fileinfo = FileInfo(filename) line = clean_lines.lines[linenum] # "include" should use the new style "foo/bar.h" instead of just "bar.h" if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): error(filename, linenum, 'build/include', 4, 'Include the directory when naming .h files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's # not. match = _RE_PATTERN_INCLUDE.search(line) if match: include = match.group(2) is_system = (match.group(1) == '<') if include in include_state: error(filename, linenum, 'build/include', 4, '"%s" already included at %s:%s' % (include, filename, include_state[include])) else: include_state[include] = linenum # We want to ensure that headers appear in the right order: # 1) for foo.cc, foo.h (preferred location) # 2) c system files # 3) cpp system files # 4) for foo.cc, foo.h (deprecated location) # 5) other google headers # # We classify each include statement as one of those 5 types # using a number of techniques. The include_state object keeps # track of the highest type seen, and complains if we see a # lower type after that. error_message = include_state.CheckNextIncludeOrder( _ClassifyInclude(fileinfo, include, is_system)) if error_message: error(filename, linenum, 'build/include_order', 4, '%s. Should be: %s.h, c system, c++ system, other.' % (error_message, fileinfo.BaseName())) if not include_state.IsInAlphabeticalOrder(include): error(filename, linenum, 'build/include_alpha', 4, 'Include "%s" not in alphabetical order' % include) # Look for any of the stream classes that are part of standard C++. match = _RE_PATTERN_INCLUDE.match(line) if match: include = match.group(2) if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): # Many unit tests use cout, so we exempt them. if not _IsTestFilename(filename): error(filename, linenum, 'readability/streams', 3, 'Streams are highly discouraged.') def _GetTextInside(text, start_pattern): """Retrieves all the text between matching open and close parentheses. Given a string of lines and a regular expression string, retrieve all the text following the expression and between opening punctuation symbols like (, [, or {, and the matching close-punctuation symbol. This properly nested occurrences of the punctuations, so for the text like printf(a(), b(c())); a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. start_pattern must match string having an open punctuation symbol at the end. Args: text: The lines to extract text. Its comments and strings must be elided. It can be single line and can span multiple lines. start_pattern: The regexp string indicating where to start extracting the text. Returns: The extracted text. None if either the opening string or ending punctuation could not be found. """ # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably # rewritten to use _GetTextInside (and use inferior regexp matching today). # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} closing_punctuation = set(itervalues(matching_punctuation)) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) if not match: # start_pattern not found in text. return None start_position = match.end(0) assert start_position > 0, ( 'start_pattern must ends with an opening punctuation.') assert text[start_position - 1] in matching_punctuation, ( 'start_pattern must ends with an opening punctuation.') # Stack of closing punctuations we expect to have in text after position. punctuation_stack = [matching_punctuation[text[start_position - 1]]] position = start_position while punctuation_stack and position < len(text): if text[position] == punctuation_stack[-1]: punctuation_stack.pop() elif text[position] in closing_punctuation: # A closing punctuation without matching opening punctuations. return None elif text[position] in matching_punctuation: punctuation_stack.append(matching_punctuation[text[position]]) position += 1 if punctuation_stack: # Opening punctuations left without matching close-punctuations. return None # punctuations match. return text[start_position:position - 1] def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, error): """Checks rules from the 'C++ language rules' section of cppguide.html. Some of these rules are hard to test (function overloading, using uint32 inappropriately), but we do the best we can. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ # If the line is empty or consists of entirely a comment, no need to # check it. line = clean_lines.elided[linenum] if not line: return match = _RE_PATTERN_INCLUDE.search(line) if match: CheckIncludeLine(filename, clean_lines, linenum, include_state, error) return # Create an extended_line, which is the concatenation of the current and # next lines, for more effective checking of code that may span more than one # line. if linenum + 1 < clean_lines.NumLines(): extended_line = line + clean_lines.elided[linenum + 1] else: extended_line = line # Make Windows paths like Unix. fullname = os.path.abspath(filename).replace('\\', '/') # TODO(unknown): figure out if they're using default arguments in fn proto. # Check for non-const references in functions. This is tricky because & # is also used to take the address of something. We allow <> for templates, # (ignoring whatever is between the braces) and : for classes. # These are complicated re's. They try to capture the following: # paren (for fn-prototype start), typename, &, varname. For the const # version, we're willing for const to be before typename or after # Don't check the implementation on same line. fnline = line.split('{', 1)[0] if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', fnline))): # We allow non-const references in a few standard places, like functions # called "swap()" or iostream operators like "<<" or ">>". if not Search( r'(swap|Swap|operator[<>][<>])\s*\(\s*(?:[\w:]|<.*>)+\s*&', fnline): error(filename, linenum, 'runtime/references', 2, 'Is this a non-const reference? ' 'If so, make const or use a pointer.') # Check to see if they're using an conversion function cast. # I just try to capture the most common basic types, though there are more. # Parameterless conversion functions, such as bool(), are allowed as they are # probably a member operator declaration or default constructor. match = Search( r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) if match: # gMock methods are defined using some variant of MOCK_METHODx(name, type) # where type may be float(), int(string), etc. Without context they are # virtually indistinguishable from int(x) casts. Likewise, gMock's # MockCallback takes a template parameter of the form return_type(arg_type), # which looks much like the cast we're trying to detect. if (match.group(1) is None and # If new operator, then this isn't a cast not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or Match(r'^\s*MockCallback<.*>', line))): error(filename, linenum, 'readability/casting', 4, 'Using deprecated casting style. ' 'Use static_cast<%s>(...) instead' % match.group(2)) CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'static_cast', r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) # This doesn't catch all cases. Consider (const char * const)"hello". # # (char *) "foo" should always be a const_cast (reinterpret_cast won't # compile). if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): pass else: # Check pointer casts for other than string constants CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) # In addition, we look for people taking the address of a cast. This # is dangerous -- casts can assign to temporaries, so the pointer doesn't # point where you think. if Search( r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): error(filename, linenum, 'runtime/casting', 4, ('Are you taking an address of a cast? ' 'This is dangerous: could be a temp var. ' 'Take the address before doing the cast, rather than after')) # Check for people declaring static/global STL strings at the top level. # This is dangerous because the C++ language does not guarantee that # globals with constructors are initialized before the first access. match = Match( r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', line) # Make sure it's not a function. # Function template specialization looks like: "string foo(...". # Class template definitions look like: "string Foo::Method(...". if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3)): error(filename, linenum, 'runtime/string', 4, 'For a static/global string constant, use a C style string instead: ' '"%schar %s[]".' % (match.group(1), match.group(2))) # Check that we're not using RTTI outside of testing code. if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): error(filename, linenum, 'runtime/rtti', 5, 'Do not use dynamic_cast<>. If you need to cast within a class ' "hierarchy, use static_cast<> to upcast. Google doesn't support " 'RTTI.') if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): error(filename, linenum, 'runtime/init', 4, 'You seem to be initializing a member variable with itself.') if file_extension == 'h': # TODO(unknown): check that 1-arg constructors are explicit. # How to tell it's a constructor? # (handled in CheckForNonStandardConstructs for now) # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS # (level 1 error) pass # Check if people are using the verboten C basic types. The only exception # we regularly allow is "unsigned short port" for port. if Search(r'\bshort port\b', line): if not Search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') else: match = Search(r'\b(short|long(?! +double)|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) # When snprintf is used, the second argument shouldn't be a literal. match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) if match and match.group(2) != '0': # If 2nd arg is zero, snprintf is used to calculate size. error(filename, linenum, 'runtime/printf', 3, 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' 'to snprintf.' % (match.group(1), match.group(2))) # Check if some verboten C functions are being used. if Search(r'\bsprintf\b', line): error(filename, linenum, 'runtime/printf', 5, 'Never use sprintf. Use snprintf instead.') match = Search(r'\b(strcpy|strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Almost always, snprintf is better than %s' % match.group(1)) if Search(r'\bsscanf\b', line): error(filename, linenum, 'runtime/printf', 1, 'sscanf can be ok, but is slow and can overflow buffers.') # Check if some verboten operator overloading is going on # TODO(unknown): catch out-of-line unary operator&: # class X {}; # int operator&(const X& x) { return 42; } // unary operator& # The trick is it's hard to tell apart from binary operator&: # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& if Search(r'\boperator\s*&\s*\(\s*\)', line): error(filename, linenum, 'runtime/operator', 4, 'Unary operator& is dangerous. Do not use it.') # Check for suspicious usage of "if" like # } if (a == b) { if Search(r'\}\s*if\s*\(', line): error(filename, linenum, 'readability/braces', 4, 'Did you mean "else if"? If not, start a new line for "if".') # Check for potential format string bugs like printf(foo). # We constrain the pattern not to pick things like DocidForPrintf(foo). # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) # TODO(sugawarayu): Catch the following case. Need to change the calling # convention of the whole function to process multiple line to handle it. # printf( # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') if printf_args: match = Match(r'([\w.\->()]+)$', printf_args) if match: function_name = re.search(r'\b((?:string)?printf)\s*\(', line, re.I).group(1) error(filename, linenum, 'runtime/printf', 4, 'Potential format string bug. Do %s("%%s", %s) instead.' % (function_name, match.group(1))) # Check for potential memset bugs like memset(buf, sizeof(buf), 0). match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): error(filename, linenum, 'runtime/memset', 4, 'Did you mean "memset(%s, 0, %s)"?' % (match.group(1), match.group(2))) if Search(r'\busing namespace\b', line): error(filename, linenum, 'build/namespaces', 5, 'Do not use namespace using-directives. ' 'Use using-declarations instead.') # Detect variable-length arrays. match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) if (match and match.group(2) != 'return' and match.group(2) != 'delete' and match.group(3).find(']') == -1): # Split the size using space and arithmetic operators as delimiters. # If any of the resulting tokens are not compile time constants then # report the error. tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) is_const = True skip_next = False for tok in tokens: if skip_next: skip_next = False continue if Search(r'sizeof\(.+\)', tok): continue if Search(r'arraysize\(\w+\)', tok): continue tok = tok.lstrip('(') tok = tok.rstrip(')') if not tok: continue if Match(r'\d+', tok): continue if Match(r'0[xX][0-9a-fA-F]+', tok): continue if Match(r'k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. if tok.startswith('sizeof'): skip_next = True continue is_const = False break if not is_const: error(filename, linenum, 'runtime/arrays', 1, 'Do not use variable-length arrays. Use an appropriately named ' "('k' followed by CamelCase) compile-time constant for the size.") # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing # in the class declaration. match = Match( (r'\s*' r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' r'\(.*\);$'), line) if match and linenum + 1 < clean_lines.NumLines(): next_line = clean_lines.elided[linenum + 1] # We allow some, but not all, declarations of variables to be present # in the statement that defines the class. The [\w\*,\s]* fragment of # the regular expression below allows users to declare instances of # the class or pointers to instances, but not less common types such # as function pointers or arrays. It's a tradeoff between allowing # reasonable code and avoiding trying to parse more C++ using regexps. if not Search(r'^\s*}[\w\*,\s]*;', next_line): error(filename, linenum, 'readability/constructors', 3, match.group(1) + ' should be the last thing in the class') # Check for use of unnamed namespaces in header files. Registration # macros are typically OK, so we allow use of "namespace {" on lines # that end with backslashes. if (file_extension == 'h' and Search(r'\bnamespace\s*{', line) and line[-1] != '\\'): error(filename, linenum, 'build/namespaces', 4, 'Do not use unnamed namespaces in header files. See ' 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, error): """Checks for a C-style cast by looking for the pattern. This also handles sizeof(type) warnings, due to similarity of content. Args: filename: The name of the current file. linenum: The number of the line to check. line: The line of code to check. raw_line: The raw line of code to check, with comments. cast_type: The string for the C++ cast to recommend. This is either reinterpret_cast, static_cast, or const_cast, depending. pattern: The regular expression used to find C-style casts. error: The function to call with any errors found. Returns: True if an error was emitted. False otherwise. """ match = Search(pattern, line) if not match: return False # e.g., sizeof(int) sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) if sizeof_match: error(filename, linenum, 'runtime/sizeof', 1, 'Using sizeof(type). Use sizeof(varname) instead if possible') return True remainder = line[match.end(0):] # The close paren is for function pointers as arguments to a function. # eg, void foo(void (*bar)(int)); # The semicolon check is a more basic function check; also possibly a # function pointer typedef. # eg, void foo(int); or void foo(int) const; # The equals check is for function pointer assignment. # eg, void *(*foo)(int) = ... # The > is for MockCallback<...> ... # # Right now, this will only catch cases where there's a single argument, and # it's unnamed. It should probably be expanded to check for multiple # arguments with some unnamed. function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) if function_match: if (not function_match.group(3) or function_match.group(3) == ';' or ('MockCallback<' not in raw_line and '/*' not in raw_line)): error(filename, linenum, 'readability/function', 3, 'All parameters should be named in a function') return True # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, 'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1))) return True _HEADERS_CONTAINING_TEMPLATES = ( ('', ('deque',)), ('', ('unary_function', 'binary_function', 'plus', 'minus', 'multiplies', 'divides', 'modulus', 'negate', 'equal_to', 'not_equal_to', 'greater', 'less', 'greater_equal', 'less_equal', 'logical_and', 'logical_or', 'logical_not', 'unary_negate', 'not1', 'binary_negate', 'not2', 'bind1st', 'bind2nd', 'pointer_to_unary_function', 'pointer_to_binary_function', 'ptr_fun', 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', 'mem_fun_ref_t', 'const_mem_fun_t', 'const_mem_fun1_t', 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', 'mem_fun_ref', )), ('', ('numeric_limits',)), ('', ('list',)), ('', ('map', 'multimap',)), ('', ('allocator',)), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('pair',)), ('', ('vector',)), # gcc extensions. # Note: std::hash is their hash, ::hash is our hash ('', ('hash_map', 'hash_multimap',)), ('', ('hash_set', 'hash_multiset',)), ('', ('slist',)), ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') _re_pattern_algorithm_header = [] for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', 'transform'): # Match max(..., ...), max(..., ...), but not foo->max, foo.max or # type::max(). _re_pattern_algorithm_header.append( (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), _template, '')) _re_pattern_templates = [] for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: for _template in _templates: _re_pattern_templates.append( (re.compile(r'(\<|\b)' + _template + r'\s*\<'), _template + '<>', _header)) def FilesBelongToSameModule(filename_cc, filename_h): """Check if these two filenames belong to the same module. The concept of a 'module' here is a as follows: foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the same 'module' if they are in the same directory. some/path/public/xyzzy and some/path/internal/xyzzy are also considered to belong to the same module here. If the filename_cc contains a longer path than the filename_h, for example, '/absolute/path/to/base/sysinfo.cc', and this file would include 'base/sysinfo.h', this function also produces the prefix needed to open the header. This is used by the caller of this function to more robustly open the header file. We don't have access to the real include paths in this context, so we need this guesswork here. Known bugs: tools/base/bar.cc and base/bar.h belong to the same module according to this implementation. Because of this, this function gives some false positives. This should be sufficiently rare in practice. Args: filename_cc: is the path for the .cc file filename_h: is the path for the header path Returns: Tuple with a bool and a string: bool: True if filename_cc and filename_h belong to the same module. string: the additional prefix needed to open the header file. """ if not filename_cc.endswith('.cc'): return (False, '') filename_cc = filename_cc[:-len('.cc')] if filename_cc.endswith('_unittest'): filename_cc = filename_cc[:-len('_unittest')] elif filename_cc.endswith('_test'): filename_cc = filename_cc[:-len('_test')] filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') if not filename_h.endswith('.h'): return (False, '') filename_h = filename_h[:-len('.h')] if filename_h.endswith('-inl'): filename_h = filename_h[:-len('-inl')] filename_h = filename_h.replace('/public/', '/') filename_h = filename_h.replace('/internal/', '/') files_belong_to_same_module = filename_cc.endswith(filename_h) common_path = '' if files_belong_to_same_module: common_path = filename_cc[:-len(filename_h)] return files_belong_to_same_module, common_path def UpdateIncludeState(filename, include_state, io=codecs): """Fill up the include_state with new includes found from the file. Args: filename: the name of the header to read. include_state: an _IncludeState instance in which the headers are inserted. io: The io factory to use to read the file. Provided for testability. Returns: True if a header was succesfully added. False otherwise. """ headerfile = None try: headerfile = io.open(filename, 'r', 'utf8', 'replace') except IOError: return False linenum = 0 for line in headerfile: linenum += 1 clean_line = CleanseComments(line) match = _RE_PATTERN_INCLUDE.search(clean_line) if match: include = match.group(2) # The value formatting is cute, but not really used right now. # What matters here is that the key is in include_state. include_state.setdefault(include, '%s:%d' % (filename, linenum)) return True def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs): """Reports for missing stl includes. This function will output warnings to make sure you are including the headers necessary for the stl containers and functions that you use. We only give one reason to include a header. For example, if you use both equal_to<> and less<> in a .h file, only one (the latter in the file) of these will be reported as a reason to include the . Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. include_state: An _IncludeState instance. error: The function to call with any errors found. io: The IO factory to use to read the header file. Provided for unittest injection. """ required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } for linenum in range(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue # String is special -- it is a non-templatized type in STL. matched = _RE_PATTERN_STRING.search(line) if matched: # Don't warn about strings in non-STL namespaces: # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): required[''] = (linenum, 'string') for pattern, template, header in _re_pattern_algorithm_header: if pattern.search(line): required[header] = (linenum, template) # The following function is just a speed up, no semantics are changed. if not '<' in line: # Reduces the cpu time usage by skipping lines. continue for pattern, template, header in _re_pattern_templates: if pattern.search(line): required[header] = (linenum, template) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. # Let's copy the include_state so it is only messed up within this function. include_state = include_state.copy() # Did we find the header for this file (if any) and succesfully load it? header_found = False # Use the absolute path so that matching works properly. abs_filename = FileInfo(filename).FullName() # For Emacs's flymake. # If cpplint is invoked from Emacs's flymake, a temporary file is generated # by flymake and that file name might end with '_flymake.cc'. In that case, # restore original file name here so that the corresponding header file can be # found. # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' # instead of 'foo_flymake.h' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) # include_state is modified during iteration, so we iterate over a copy of # the keys. header_keys = list(include_state.keys()) for header in header_keys: (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) fullpath = common_path + header if same_module and UpdateIncludeState(fullpath, include_state, io): header_found = True # If we can't find the header file for a .cc, assume it's because we don't # know where to look. In that case we'll give up as we're not sure they # didn't include it in the .h file. # TODO(unknown): Do a better job of finding .h files so we are confident that # not having the .h file means there isn't one. if filename.endswith('.cc') and not header_found: return # All the lines have been processed, report the errors found. for required_header_unstripped in required: template = required[required_header_unstripped][1] if required_header_unstripped.strip('<>"') not in include_state: error(filename, required[required_header_unstripped][0], 'build/include_what_you_use', 4, 'Add #include ' + required_header_unstripped + ' for ' + template) _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): """Check that make_pair's template arguments are deduced. G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are specified explicitly, and such use isn't intended in any case. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) if match: error(filename, linenum, 'build/explicit_make_pair', 4, # 4 = high confidence 'Omit template arguments from make_pair OR use pair directly OR' ' if appropriate, construct a pair directly') def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, class_state, error, extra_check_functions=[]): """Processes a single line in the file. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. clean_lines: An array of strings, each representing a line of the file, with comments stripped. line: Number of line being processed. include_state: An _IncludeState instance in which the headers are inserted. function_state: A _FunctionState instance which counts function lines, etc. class_state: A _ClassState instance which maintains information about the current stack of nested class declarations being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ raw_lines = clean_lines.raw_lines ParseNolintSuppressions(filename, raw_lines[line], line, error) CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckStyle(filename, clean_lines, line, file_extension, class_state, error) CheckLanguage(filename, clean_lines, line, file_extension, include_state, error) CheckForNonStandardConstructs(filename, clean_lines, line, class_state, error) CheckPosixThreading(filename, clean_lines, line, error) CheckInvalidIncrement(filename, clean_lines, line, error) CheckMakePairUsesDeduction(filename, clean_lines, line, error) for check_fn in extra_check_functions: check_fn(filename, clean_lines, line, error) def ProcessFileData(filename, file_extension, lines, error, extra_check_functions=[]): """Performs lint checks and reports any errors to the given error function. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. lines: An array of strings, each representing a line of the file, with the last element being empty if the file is terminated with a newline. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) include_state = _IncludeState() function_state = _FunctionState() class_state = _ClassState() ResetNolintSuppressions() CheckForCopyright(filename, lines, error) if file_extension == 'h': CheckForHeaderGuard(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines) for line in range(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, class_state, error, extra_check_functions) class_state.CheckFinished(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. CheckForUnicodeReplacementCharacters(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error) def ProcessFile(filename, vlevel, extra_check_functions=[]): """Does google-lint on a single file. Args: filename: The name of the file to parse. vlevel: The level of errors to report. Every error of confidence >= verbose_level will be reported. 0 is a good default. extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ _SetVerboseLevel(vlevel) try: # Support the UNIX convention of using "-" for stdin. Note that # we are not opening the file with universal newline support # (which codecs doesn't support anyway), so the resulting lines do # contain trailing '\r' characters if we are reading a file that # has CRLF endings. # If after the split a trailing '\r' is present, it is removed # below. If it is not expected to be present (i.e. os.linesep != # '\r\n' as in Windows), a warning is issued below if this file # is processed. if filename == '-': lines = codecs.StreamReaderWriter(sys.stdin, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace').read().split('\n') else: lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') carriage_return_found = False # Remove trailing '\r'. for linenum in range(len(lines)): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') carriage_return_found = True except IOError: sys.stderr.write( "Skipping input '%s': Can't open for reading\n" % filename) return # Note, if no dot is found, this will give the entire filename as the ext. file_extension = filename[filename.rfind('.') + 1:] # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if (filename != '-' and file_extension not in EXTENSIONS): pass # sys.stderr.write('Ignoring %s; extension not in %s\n' % (filename, EXTENSIONS)) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) if carriage_return_found and os.linesep != '\r\n': # Use 0 for linenum since outputting only one error for potentially # several lines. Error(filename, 0, 'whitespace/newline', 1, 'One or more unexpected \\r (^M) found;' 'better to use only a \\n') sys.stderr.write('Done processing %s\n' % filename) def PrintUsage(message): """Prints a brief usage string and exits, optionally with an error message. Args: message: The optional error message. """ sys.stderr.write(_USAGE) if message: sys.exit('\nFATAL ERROR: ' + message) else: sys.exit(1) def PrintCategories(): """Prints a list of all the error-categories used by error messages. These are the categories used to filter messages via --filter. """ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) sys.exit(0) def ParseArguments(args): """Parses the command line arguments. This may set the output format and verbosity level as side-effects. Args: args: The command line arguments: Returns: The list of filenames to lint. """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=']) except getopt.GetoptError: PrintUsage('Invalid arguments.') verbosity = _VerboseLevel() output_format = _OutputFormat() filters = '' counting_style = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) elif opt == '--output': if not val in ('emacs', 'vs7'): PrintUsage('The only allowed output formats are emacs and vs7.') output_format = val elif opt == '--verbose': verbosity = int(val) elif opt == '--filter': filters = val if not filters: PrintCategories() elif opt == '--counting': if val not in ('total', 'toplevel', 'detailed'): PrintUsage('Valid counting options are total, toplevel, and detailed') counting_style = val if not filenames: PrintUsage('No files were specified.') _SetOutputFormat(output_format) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) return filenames def main(): filenames = ParseArguments(sys.argv[1:]) backup_err = sys.stderr try: # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. sys.stderr = codecs.StreamReader(sys.stderr, 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) _cpplint_state.PrintErrorCounts() finally: sys.stderr = backup_err sys.exit(_cpplint_state.error_count > 0) if __name__ == '__main__': main() ================================================ FILE: utils/git-hooks/pep8.py ================================================ #!/usr/bin/env python # pep8.py - Check Python source code formatting, according to PEP 8 # Copyright (C) 2006-2009 Johann C. Rocholl # Copyright (C) 2009-2013 Florent Xicluna # # 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. r""" Check Python source code formatting, according to PEP 8: http://www.python.org/dev/peps/pep-0008/ For usage and a list of options, try this: $ python pep8.py -h This program and its regression test suite live here: http://github.com/jcrocholl/pep8 Groups of errors and warnings: E errors W warnings 100 indentation 200 whitespace 300 blank lines 400 imports 500 line length 600 deprecation 700 statements 900 syntax error """ __version__ = '1.4.6' import os import sys import re import time import inspect import keyword import tokenize from optparse import OptionParser from fnmatch import fnmatch try: from configparser import RawConfigParser from io import TextIOWrapper except ImportError: from ConfigParser import RawConfigParser DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__' DEFAULT_IGNORE = 'E123,E226,E24' if sys.platform == 'win32': DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') else: DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), 'pep8') PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8') TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite') MAX_LINE_LENGTH = 79 REPORT_FORMAT = { 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s', 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s', } PyCF_ONLY_AST = 1024 SINGLETONS = frozenset(['False', 'None', 'True']) KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-']) WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) WS_NEEDED_OPERATORS = frozenset([ '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>', '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=']) WHITESPACE = frozenset(' \t') SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT]) BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] INDENT_REGEX = re.compile(r'([ \t]*)') RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,') RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+') ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') DOCSTRING_REGEX = re.compile(r'u?r?["\']') EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)') COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type' r'|\s*\(\s*([^)]*[^ )])\s*\))') KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') LAMBDA_REGEX = re.compile(r'\blambda\b') HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') # Work around Python < 2.6 behaviour, which does not generate NL after # a comment which is on a line by itself. COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' ############################################################################## # Plugins (check functions) for physical lines ############################################################################## def tabs_or_spaces(physical_line, indent_char): r""" Never mix tabs and spaces. The most popular way of indenting Python is with spaces only. The second-most popular way is with tabs only. Code indented with a mixture of tabs and spaces should be converted to using spaces exclusively. When invoking the Python command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended! Okay: if a == 0:\n a = 1\n b = 1 E101: if a == 0:\n a = 1\n\tb = 1 """ indent = INDENT_REGEX.match(physical_line).group(1) for offset, char in enumerate(indent): if char != indent_char: return offset, "E101 indentation contains mixed spaces and tabs" def tabs_obsolete(physical_line): r""" For new projects, spaces-only are strongly recommended over tabs. Most editors have features that make this easy to do. Okay: if True:\n return W191: if True:\n\treturn """ indent = INDENT_REGEX.match(physical_line).group(1) if '\t' in indent: return indent.index('\t'), "W191 indentation contains tabs" def trailing_whitespace(physical_line): r""" JCR: Trailing whitespace is superfluous. FBM: Except when it occurs as part of a blank line (i.e. the line is nothing but whitespace). According to Python docs[1] a line with only whitespace is considered a blank line, and is to be ignored. However, matching a blank line to its indentation level avoids mistakenly terminating a multi-line statement (e.g. class declaration) when pasting code into the standard Python interpreter. [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines The warning returned varies on whether the line itself is blank, for easier filtering for those who want to indent their blank lines. Okay: spam(1)\n# W291: spam(1) \n# W293: class Foo(object):\n \n bang = 12 """ physical_line = physical_line.rstrip('\n') # chr(10), newline physical_line = physical_line.rstrip('\r') # chr(13), carriage return physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L stripped = physical_line.rstrip(' \t\v') if physical_line != stripped: if stripped: return len(stripped), "W291 trailing whitespace" else: return 0, "W293 blank line contains whitespace" def trailing_blank_lines(physical_line, lines, line_number): r""" JCR: Trailing blank lines are superfluous. Okay: spam(1) W391: spam(1)\n """ if not physical_line.rstrip() and line_number == len(lines): return 0, "W391 blank line at end of file" def missing_newline(physical_line): """ JCR: The last line should have a newline. Reports warning W292. """ if physical_line.rstrip() == physical_line: return len(physical_line), "W292 no newline at end of file" def maximum_line_length(physical_line, max_line_length): """ Limit all lines to a maximum of 79 characters. There are still many devices around that are limited to 80 character lines; plus, limiting windows to 80 characters makes it possible to have several windows side-by-side. The default wrapping on such devices looks ugly. Therefore, please limit all lines to a maximum of 79 characters. For flowing long blocks of text (docstrings or comments), limiting the length to 72 characters is recommended. Reports error E501. """ line = physical_line.rstrip() length = len(line) if length > max_line_length and not noqa(line): if hasattr(line, 'decode'): # Python 2 # The line could contain multi-byte characters try: length = len(line.decode('utf-8')) except UnicodeError: pass if length > max_line_length: return (max_line_length, "E501 line too long " "(%d > %d characters)" % (length, max_line_length)) ############################################################################## # Plugins (check functions) for logical lines ############################################################################## def blank_lines(logical_line, blank_lines, indent_level, line_number, previous_logical, previous_indent_level): r""" Separate top-level function and class definitions with two blank lines. Method definitions inside a class are separated by a single blank line. Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations). Use blank lines in functions, sparingly, to indicate logical sections. Okay: def a():\n pass\n\n\ndef b():\n pass Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass E301: class Foo:\n b = 0\n def bar():\n pass E302: def a():\n pass\n\ndef b(n):\n pass E303: def a():\n pass\n\n\n\ndef b(n):\n pass E303: def a():\n\n\n\n pass E304: @decorator\n\ndef a():\n pass """ if line_number < 3 and not previous_logical: return # Don't expect blank lines before the first line if previous_logical.startswith('@'): if blank_lines: yield 0, "E304 blank lines found after function decorator" elif blank_lines > 2 or (indent_level and blank_lines == 2): yield 0, "E303 too many blank lines (%d)" % blank_lines elif logical_line.startswith(('def ', 'class ', '@')): if indent_level: if not (blank_lines or previous_indent_level < indent_level or DOCSTRING_REGEX.match(previous_logical)): yield 0, "E301 expected 1 blank line, found 0" elif blank_lines != 2: yield 0, "E302 expected 2 blank lines, found %d" % blank_lines def extraneous_whitespace(logical_line): """ Avoid extraneous whitespace in the following situations: - Immediately inside parentheses, brackets or braces. - Immediately before a comma, semicolon, or colon. Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) E201: spam(ham[ 1], {eggs: 2}) E201: spam(ham[1], { eggs: 2}) E202: spam(ham[1], {eggs: 2} ) E202: spam(ham[1 ], {eggs: 2}) E202: spam(ham[1], {eggs: 2 }) E203: if x == 4: print x, y; x, y = y , x E203: if x == 4: print x, y ; x, y = y, x E203: if x == 4 : print x, y; x, y = y, x """ line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): text = match.group() char = text.strip() found = match.start() if text == char + ' ': # assert char in '([{' yield found + 1, "E201 whitespace after '%s'" % char elif line[found - 1] != ',': code = ('E202' if char in '}])' else 'E203') # if char in ',;:' yield found, "%s whitespace before '%s'" % (code, char) def whitespace_around_keywords(logical_line): r""" Avoid extraneous whitespace around keywords. Okay: True and False E271: True and False E272: True and False E273: True and\tFalse E274: True\tand False """ for match in KEYWORD_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E274 tab before keyword" elif len(before) > 1: yield match.start(1), "E272 multiple spaces before keyword" if '\t' in after: yield match.start(2), "E273 tab after keyword" elif len(after) > 1: yield match.start(2), "E271 multiple spaces after keyword" def missing_whitespace(logical_line): """ JCR: Each comma, semicolon or colon should be followed by whitespace. Okay: [a, b] Okay: (3,) Okay: a[1:4] Okay: a[:4] Okay: a[1:] Okay: a[1:4:2] E231: ['a','b'] E231: foo(bar,baz) E231: [{'a':'b'}] """ line = logical_line for index in range(len(line) - 1): char = line[index] if char in ',;:' and line[index + 1] not in WHITESPACE: before = line[:index] if char == ':' and before.count('[') > before.count(']') and \ before.rfind('{') < before.rfind('['): continue # Slice syntax, no space required if char == ',' and line[index + 1] == ')': continue # Allow tuple with only one element: (3,) yield index, "E231 missing whitespace after '%s'" % char def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level): r""" Use 4 spaces per indentation level. For really old code that you don't want to mess up, you can continue to use 8-space tabs. Okay: a = 1 Okay: if a == 0:\n a = 1 E111: a = 1 Okay: for item in items:\n pass E112: for item in items:\npass Okay: a = 1\nb = 2 E113: a = 1\n b = 2 """ if indent_char == ' ' and indent_level % 4: yield 0, "E111 indentation is not a multiple of four" indent_expect = previous_logical.endswith(':') if indent_expect and indent_level <= previous_indent_level: yield 0, "E112 expected an indented block" if indent_level > previous_indent_level and not indent_expect: yield 0, "E113 unexpected indentation" def continued_indentation(logical_line, tokens, indent_level, hang_closing, noqa, verbose): r""" Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent. When using a hanging indent the following considerations should be applied: - there should be no arguments on the first line, and - further indentation should be used to clearly distinguish itself as a continuation line. Okay: a = (\n) E123: a = (\n ) Okay: a = (\n 42) E121: a = (\n 42) E122: a = (\n42) E123: a = (\n 42\n ) E124: a = (24,\n 42\n) E125: if (a or\n b):\n pass E126: a = (\n 42) E127: a = (24,\n 42) E128: a = (24,\n 42) """ first_row = tokens[0][2][0] nrows = 1 + tokens[-1][2][0] - first_row if noqa or nrows == 1: return # indent_next tells us whether the next block is indented; assuming # that it is indented by 4 spaces, then we should not allow 4-space # indents on the final continuation line; in turn, some other # indents are allowed to have an extra 4 spaces. indent_next = logical_line.endswith(':') row = depth = 0 # remember how many brackets were opened on each line parens = [0] * nrows # relative indents of physical lines rel_indent = [0] * nrows # visual indents indent_chances = {} last_indent = tokens[0][2] indent = [last_indent[1]] if verbose >= 3: print(">>> " + tokens[0][4].rstrip()) for token_type, text, start, end, line in tokens: newline = row < start[0] - first_row if newline: row = start[0] - first_row newline = (not last_token_multiline and token_type not in (tokenize.NL, tokenize.NEWLINE)) if newline: # this is the beginning of a continuation line. last_indent = start if verbose >= 3: print("... " + line.rstrip()) # record the initial indent. rel_indent[row] = expand_indent(line) - indent_level if depth: # a bracket expression in a continuation line. # find the line that it was opened on for open_row in range(row - 1, -1, -1): if parens[open_row]: break else: # an unbracketed continuation line (ie, backslash) open_row = 0 hang = rel_indent[row] - rel_indent[open_row] close_bracket = (token_type == tokenize.OP and text in ']})') visual_indent = (not close_bracket and hang > 0 and indent_chances.get(start[1])) if close_bracket and indent[depth]: # closing bracket for visual indent if start[1] != indent[depth]: yield (start, "E124 closing bracket does not match " "visual indentation") elif close_bracket and not hang: # closing bracket matches indentation of opening bracket's line if hang_closing: yield start, "E133 closing bracket is missing indentation" elif visual_indent is True: # visual indent is verified if not indent[depth]: indent[depth] = start[1] elif visual_indent in (text, str): # ignore token lined up with matching one from a previous line pass elif indent[depth] and start[1] < indent[depth]: # visual indent is broken yield (start, "E128 continuation line " "under-indented for visual indent") elif hang == 4 or (indent_next and rel_indent[row] == 8): # hanging indent is verified if close_bracket and not hang_closing: yield (start, "E123 closing bracket does not match " "indentation of opening bracket's line") else: # indent is broken if hang <= 0: error = "E122", "missing indentation or outdented" elif indent[depth]: error = "E127", "over-indented for visual indent" elif hang % 4: error = "E121", "indentation is not a multiple of four" else: error = "E126", "over-indented for hanging indent" yield start, "%s continuation line %s" % error # look for visual indenting if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT) and not indent[depth]): indent[depth] = start[1] indent_chances[start[1]] = True if verbose >= 4: print("bracket depth %s indent to %s" % (depth, start[1])) # deal with implicit string concatenation elif (token_type in (tokenize.STRING, tokenize.COMMENT) or text in ('u', 'ur', 'b', 'br')): indent_chances[start[1]] = str # special case for the "if" statement because len("if (") == 4 elif not indent_chances and not row and not depth and text == 'if': indent_chances[end[1] + 1] = True # keep track of bracket depth if token_type == tokenize.OP: if text in '([{': depth += 1 indent.append(0) parens[row] += 1 if verbose >= 4: print("bracket depth %s seen, col %s, visual min = %s" % (depth, start[1], indent[depth])) elif text in ')]}' and depth > 0: # parent indents should not be more than this one prev_indent = indent.pop() or last_indent[1] for d in range(depth): if indent[d] > prev_indent: indent[d] = 0 for ind in list(indent_chances): if ind >= prev_indent: del indent_chances[ind] depth -= 1 if depth: indent_chances[indent[depth]] = True for idx in range(row, -1, -1): if parens[idx]: parens[idx] -= 1 rel_indent[row] = rel_indent[idx] break assert len(indent) == depth + 1 if start[1] not in indent_chances: # allow to line up tokens indent_chances[start[1]] = text last_token_multiline = (start[0] != end[0]) if indent_next and expand_indent(line) == indent_level + 4: yield (last_indent, "E125 continuation line does not distinguish " "itself from next logical line") def whitespace_before_parameters(logical_line, tokens): """ Avoid extraneous whitespace in the following situations: - Immediately before the open parenthesis that starts the argument list of a function call. - Immediately before the open parenthesis that starts an indexing or slicing. Okay: spam(1) E211: spam (1) Okay: dict['key'] = list[index] E211: dict ['key'] = list[index] E211: dict['key'] = list [index] """ prev_type, prev_text, __, prev_end, __ = tokens[0] for index in range(1, len(tokens)): token_type, text, start, end, __ = tokens[index] if (token_type == tokenize.OP and text in '([' and start != prev_end and (prev_type == tokenize.NAME or prev_text in '}])') and # Syntax "class A (B):" is allowed, but avoid it (index < 2 or tokens[index - 2][1] != 'class') and # Allow "return (a.foo for a in range(5))" not keyword.iskeyword(prev_text)): yield prev_end, "E211 whitespace before '%s'" % text prev_type = token_type prev_text = text prev_end = end def whitespace_around_operator(logical_line): r""" Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. Okay: a = 12 + 3 E221: a = 4 + 5 E222: a = 4 + 5 E223: a = 4\t+ 5 E224: a = 4 +\t5 """ for match in OPERATOR_REGEX.finditer(logical_line): before, after = match.groups() if '\t' in before: yield match.start(1), "E223 tab before operator" elif len(before) > 1: yield match.start(1), "E221 multiple spaces before operator" if '\t' in after: yield match.start(2), "E224 tab after operator" elif len(after) > 1: yield match.start(2), "E222 multiple spaces after operator" def missing_whitespace_around_operator(logical_line, tokens): r""" - Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not). - Use spaces around arithmetic operators. Okay: i = i + 1 Okay: submitted += 1 Okay: x = x * 2 - 1 Okay: hypot2 = x * x + y * y Okay: c = (a + b) * (a - b) Okay: foo(bar, key='word', *args, **kwargs) Okay: alpha[:-i] E225: i=i+1 E225: submitted +=1 E225: x = x /2 - 1 E225: z = x **y E226: c = (a+b) * (a-b) E226: hypot2 = x*x + y*y E227: c = a|b E228: msg = fmt%(errno, errmsg) """ parens = 0 need_space = False prev_type = tokenize.OP prev_text = prev_end = None for token_type, text, start, end, line in tokens: if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): # ERRORTOKEN is triggered by backticks in Python 3 continue if text in ('(', 'lambda'): parens += 1 elif text == ')': parens -= 1 if need_space: if start != prev_end: # Found a (probably) needed space if need_space is not True and not need_space[1]: yield (need_space[0], "E225 missing whitespace around operator") need_space = False elif text == '>' and prev_text in ('<', '-'): # Tolerate the "<>" operator, even if running Python 3 # Deal with Python 3's annotated return value "->" pass else: if need_space is True or need_space[1]: # A needed trailing space was not found yield prev_end, "E225 missing whitespace around operator" else: code, optype = 'E226', 'arithmetic' if prev_text == '%': code, optype = 'E228', 'modulo' elif prev_text not in ARITHMETIC_OP: code, optype = 'E227', 'bitwise or shift' yield (need_space[0], "%s missing whitespace " "around %s operator" % (code, optype)) need_space = False elif token_type == tokenize.OP and prev_end is not None: if text == '=' and parens: # Allow keyword args or defaults: foo(bar=None). pass elif text in WS_NEEDED_OPERATORS: need_space = True elif text in UNARY_OPERATORS: # Check if the operator is being used as a binary operator # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). if prev_type == tokenize.OP: binary_usage = (prev_text in '}])') elif prev_type == tokenize.NAME: binary_usage = (prev_text not in KEYWORDS) else: binary_usage = (prev_type not in SKIP_TOKENS) if binary_usage: need_space = None elif text in WS_OPTIONAL_OPERATORS: need_space = None if need_space is None: # Surrounding space is optional, but ensure that # trailing space matches opening space need_space = (prev_end, start != prev_end) elif need_space and start == prev_end: # A needed opening space was not found yield prev_end, "E225 missing whitespace around operator" need_space = False prev_type = token_type prev_text = text prev_end = end def whitespace_around_comma(logical_line): r""" Avoid extraneous whitespace in the following situations: - More than one space around an assignment (or other) operator to align it with another. Note: these checks are disabled by default Okay: a = (1, 2) E241: a = (1, 2) E242: a = (1,\t2) """ line = logical_line for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line): found = m.start() + 1 if '\t' in m.group(): yield found, "E242 tab after '%s'" % m.group()[0] else: yield found, "E241 multiple spaces after '%s'" % m.group()[0] def whitespace_around_named_parameter_equals(logical_line, tokens): """ Don't use spaces around the '=' sign when used to indicate a keyword argument or a default parameter value. Okay: def complex(real, imag=0.0): Okay: return magic(r=real, i=imag) Okay: boolean(a == b) Okay: boolean(a != b) Okay: boolean(a <= b) Okay: boolean(a >= b) E251: def complex(real, imag = 0.0): E251: return magic(r = real, i = imag) """ parens = 0 no_space = False prev_end = None message = "E251 unexpected spaces around keyword / parameter equals" for token_type, text, start, end, line in tokens: if no_space: no_space = False if start != prev_end: yield (prev_end, message) elif token_type == tokenize.OP: if text == '(': parens += 1 elif text == ')': parens -= 1 elif parens and text == '=': no_space = True if start != prev_end: yield (prev_end, message) prev_end = end def whitespace_before_inline_comment(logical_line, tokens): """ Separate inline comments by at least two spaces. An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space. Okay: x = x + 1 # Increment x Okay: x = x + 1 # Increment x E261: x = x + 1 # Increment x E262: x = x + 1 #Increment x E262: x = x + 1 # Increment x """ prev_end = (0, 0) for token_type, text, start, end, line in tokens: if token_type == tokenize.COMMENT: if not line[:start[1]].strip(): continue if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: yield (prev_end, "E261 at least two spaces before inline comment") symbol, sp, comment = text.partition(' ') if symbol not in ('#', '#:') or comment[:1].isspace(): yield start, "E262 inline comment should start with '# '" elif token_type != tokenize.NL: prev_end = end def imports_on_separate_lines(logical_line): r""" Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os Okay: from subprocess import Popen, PIPE Okay: from myclas import MyClass Okay: from foo.bar.yourclass import YourClass Okay: import myclass Okay: import foo.bar.yourclass """ line = logical_line if line.startswith('import '): found = line.find(',') if -1 < found and ';' not in line[:found]: yield found, "E401 multiple imports on one line" def compound_statements(logical_line): r""" Compound statements (multiple statements on the same line) are generally discouraged. While sometimes it's okay to put an if/for/while with a small body on the same line, never do this for multi-clause statements. Also avoid folding such long lines! Okay: if foo == 'blah':\n do_blah_thing() Okay: do_one() Okay: do_two() Okay: do_three() E701: if foo == 'blah': do_blah_thing() E701: for x in lst: total += x E701: while t < 10: t = delay() E701: if foo == 'blah': do_blah_thing() E701: else: do_non_blah_thing() E701: try: something() E701: finally: cleanup() E701: if foo == 'blah': one(); two(); three() E702: do_one(); do_two(); do_three() E703: do_four(); # useless semicolon """ line = logical_line last_char = len(line) - 1 found = line.find(':') while -1 < found < last_char: before = line[:found] if (before.count('{') <= before.count('}') and # {'a': 1} (dict) before.count('[') <= before.count(']') and # [1:2] (slice) before.count('(') <= before.count(')') and # (Python 3 annotation) not LAMBDA_REGEX.search(before)): # lambda x: x yield found, "E701 multiple statements on one line (colon)" found = line.find(':', found + 1) found = line.find(';') while -1 < found: if found < last_char: yield found, "E702 multiple statements on one line (semicolon)" else: yield found, "E703 statement ends with a semicolon" found = line.find(';', found + 1) def explicit_line_join(logical_line, tokens): r""" Avoid explicit line join between brackets. The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. E502: aaa = [123, \\n 123] E502: aaa = ("bbb " \\n "ccc") Okay: aaa = [123,\n 123] Okay: aaa = ("bbb "\n "ccc") Okay: aaa = "bbb " \\n "ccc" """ prev_start = prev_end = parens = 0 for token_type, text, start, end, line in tokens: if start[0] != prev_start and parens and backslash: yield backslash, "E502 the backslash is redundant between brackets" if end[0] != prev_end: if line.rstrip('\r\n').endswith('\\'): backslash = (end[0], len(line.splitlines()[-1]) - 1) else: backslash = None prev_start = prev_end = end[0] else: prev_start = start[0] if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in ')]}': parens -= 1 def comparison_to_singleton(logical_line, noqa): """ Comparisons to singletons like None should always be done with "is" or "is not", never the equality operators. Okay: if arg is not None: E711: if arg != None: E712: if arg == True: Also, beware of writing if x when you really mean if x is not None -- e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context! """ match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) if match: same = (match.group(1) == '==') singleton = match.group(2) msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) if singleton in ('None',): code = 'E711' else: code = 'E712' nonzero = ((singleton == 'True' and same) or (singleton == 'False' and not same)) msg += " or 'if %scond:'" % ('' if nonzero else 'not ') yield match.start(1), ("%s comparison to %s should be %s" % (code, singleton, msg)) def comparison_type(logical_line): """ Object type comparisons should always use isinstance() instead of comparing types directly. Okay: if isinstance(obj, int): E721: if type(obj) is type(1): When checking if an object is a string, keep in mind that it might be a unicode string too! In Python 2.3, str and unicode have a common base class, basestring, so you can do: Okay: if isinstance(obj, basestring): Okay: if type(a1) is type(b1): """ match = COMPARE_TYPE_REGEX.search(logical_line) if match: inst = match.group(1) if inst and isidentifier(inst) and inst not in SINGLETONS: return # Allow comparison for types which are not obvious yield match.start(), "E721 do not compare types, use 'isinstance()'" def python_3000_has_key(logical_line): r""" The {}.has_key() method is removed in the Python 3. Use the 'in' operation instead. Okay: if "alph" in d:\n print d["alph"] W601: assert d.has_key('alph') """ pos = logical_line.find('.has_key(') if pos > -1: yield pos, "W601 .has_key() is deprecated, use 'in'" def python_3000_raise_comma(logical_line): """ When raising an exception, use "raise ValueError('message')" instead of the older form "raise ValueError, 'message'". The paren-using form is preferred because when the exception arguments are long or include string formatting, you don't need to use line continuation characters thanks to the containing parentheses. The older form is removed in Python 3. Okay: raise DummyError("Message") W602: raise DummyError, "Message" """ match = RAISE_COMMA_REGEX.match(logical_line) if match and not RERAISE_COMMA_REGEX.match(logical_line): yield match.end() - 1, "W602 deprecated form of raising exception" def python_3000_not_equal(logical_line): """ != can also be written <>, but this is an obsolete usage kept for backwards compatibility only. New code should always use !=. The older syntax is removed in Python 3. Okay: if a != 'no': W603: if a <> 'no': """ pos = logical_line.find('<>') if pos > -1: yield pos, "W603 '<>' is deprecated, use '!='" def python_3000_backticks(logical_line): """ Backticks are removed in Python 3. Use repr() instead. Okay: val = repr(1 + 2) W604: val = `1 + 2` """ pos = logical_line.find('`') if pos > -1: yield pos, "W604 backticks are deprecated, use 'repr()'" ############################################################################## # Helper functions ############################################################################## if '' == ''.encode(): # Python 2: implicit encoding. def readlines(filename): f = open(filename) try: return f.readlines() finally: f.close() isidentifier = re.compile(r'[a-zA-Z_]\w*').match stdin_get_value = sys.stdin.read else: # Python 3 def readlines(filename): f = open(filename, 'rb') try: coding, lines = tokenize.detect_encoding(f.readline) f = TextIOWrapper(f, coding, line_buffering=True) return [l.decode(coding) for l in lines] + f.readlines() except (LookupError, SyntaxError, UnicodeError): f.close() # Fall back if files are improperly declared f = open(filename, encoding='latin-1') return f.readlines() finally: f.close() isidentifier = str.isidentifier def stdin_get_value(): return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() readlines.__doc__ = " Read the source code." noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search def expand_indent(line): r""" Return the amount of indentation. Tabs are expanded to the next multiple of 8. >>> expand_indent(' ') 4 >>> expand_indent('\t') 8 >>> expand_indent(' \t') 8 >>> expand_indent(' \t') 8 >>> expand_indent(' \t') 16 """ if '\t' not in line: return len(line) - len(line.lstrip()) result = 0 for char in line: if char == '\t': result = result // 8 * 8 + 8 elif char == ' ': result += 1 else: break return result def mute_string(text): """ Replace contents with 'xxx' to prevent syntax matching. >>> mute_string('"abc"') '"xxx"' >>> mute_string("'''abc'''") "'''xxx'''" >>> mute_string("r'abc'") "r'xxx'" """ # String modifiers (e.g. u or r) start = text.index(text[-1]) + 1 end = len(text) - 1 # Triple quotes if text[-3:] in ('"""', "'''"): start += 2 end -= 2 return text[:start] + 'x' * (end - start) + text[end:] def parse_udiff(diff, patterns=None, parent='.'): """Return a dictionary of matching lines.""" # For each file of the diff, the entry key is the filename, # and the value is a set of row numbers to consider. rv = {} path = nrows = None for line in diff.splitlines(): if nrows: if line[:1] != '-': nrows -= 1 continue if line[:3] == '@@ ': hunk_match = HUNK_REGEX.match(line) row, nrows = [int(g or '1') for g in hunk_match.groups()] rv[path].update(range(row, row + nrows)) elif line[:3] == '+++': path = line[4:].split('\t', 1)[0] if path[:2] == 'b/': path = path[2:] rv[path] = set() return dict([(os.path.join(parent, path), rows) for (path, rows) in rv.items() if rows and filename_match(path, patterns)]) def filename_match(filename, patterns, default=True): """ Check if patterns contains a pattern that matches filename. If patterns is unspecified, this always returns True. """ if not patterns: return default return any(fnmatch(filename, pattern) for pattern in patterns) ############################################################################## # Framework to run all checks ############################################################################## _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} def register_check(check, codes=None): """ Register a new check object. """ def _add_check(check, kind, codes, args): if check in _checks[kind]: _checks[kind][check][0].extend(codes or []) else: _checks[kind][check] = (codes or [''], args) if inspect.isfunction(check): args = inspect.getargspec(check)[0] if args and args[0] in ('physical_line', 'logical_line'): if codes is None: codes = ERRORCODE_REGEX.findall(check.__doc__ or '') _add_check(check, args[0], codes, args) elif inspect.isclass(check): if inspect.getargspec(check.__init__)[0][:2] == ['self', 'tree']: _add_check(check, 'tree', codes, None) def init_checks_registry(): """ Register all globally visible functions where the first argument name is 'physical_line' or 'logical_line'. """ mod = inspect.getmodule(register_check) for (name, function) in inspect.getmembers(mod, inspect.isfunction): register_check(function) init_checks_registry() class Checker(object): """ Load a Python source file, tokenize it, check coding style. """ def __init__(self, filename=None, lines=None, options=None, report=None, **kwargs): if options is None: options = StyleGuide(kwargs).options else: assert not kwargs self._io_error = None self._physical_checks = options.physical_checks self._logical_checks = options.logical_checks self._ast_checks = options.ast_checks self.max_line_length = options.max_line_length self.hang_closing = options.hang_closing self.verbose = options.verbose self.filename = filename if filename is None: self.filename = 'stdin' self.lines = lines or [] elif filename == '-': self.filename = 'stdin' self.lines = stdin_get_value().splitlines(True) elif lines is None: try: self.lines = readlines(filename) except IOError: exc_type, exc = sys.exc_info()[:2] self._io_error = '%s: %s' % (exc_type.__name__, exc) self.lines = [] else: self.lines = lines if self.lines: ord0 = ord(self.lines[0][0]) if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM if ord0 == 0xfeff: self.lines[0] = self.lines[0][1:] elif self.lines[0][:3] == '\xef\xbb\xbf': self.lines[0] = self.lines[0][3:] self.report = report or options.report self.report_error = self.report.error def report_invalid_syntax(self): exc_type, exc = sys.exc_info()[:2] if len(exc.args) > 1: offset = exc.args[1] if len(offset) > 2: offset = offset[1:3] else: offset = (1, 0) self.report_error(offset[0], offset[1] or 0, 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), self.report_invalid_syntax) report_invalid_syntax.__doc__ = " Check if the syntax is valid." def readline(self): """ Get the next line from the input buffer. """ self.line_number += 1 if self.line_number > len(self.lines): return '' return self.lines[self.line_number - 1] def readline_check_physical(self): """ Check and return the next physical line. This method can be used to feed tokenize.generate_tokens. """ line = self.readline() if line: self.check_physical(line) return line def run_check(self, check, argument_names): """ Run a check plugin. """ arguments = [] for name in argument_names: arguments.append(getattr(self, name)) return check(*arguments) def check_physical(self, line): """ Run all physical checks on a raw input line. """ self.physical_line = line if self.indent_char is None and line[:1] in WHITESPACE: self.indent_char = line[0] for name, check, argument_names in self._physical_checks: result = self.run_check(check, argument_names) if result is not None: offset, text = result self.report_error(self.line_number, offset, text, check) def build_tokens_line(self): """ Build a logical line from tokens. """ self.mapping = [] logical = [] comments = [] length = 0 previous = None for token in self.tokens: token_type, text = token[0:2] if token_type == tokenize.COMMENT: comments.append(text) continue if token_type in SKIP_TOKENS: continue if token_type == tokenize.STRING: text = mute_string(text) if previous: end_row, end = previous[3] start_row, start = token[2] if end_row != start_row: # different row prev_text = self.lines[end_row - 1][end - 1] if prev_text == ',' or (prev_text not in '{[(' and text not in '}])'): logical.append(' ') length += 1 elif end != start: # different column fill = self.lines[end_row - 1][end:start] logical.append(fill) length += len(fill) self.mapping.append((length, token)) logical.append(text) length += len(text) previous = token self.logical_line = ''.join(logical) self.noqa = comments and noqa(''.join(comments)) # With Python 2, if the line ends with '\r\r\n' the assertion fails # assert self.logical_line.strip() == self.logical_line def check_logical(self): """ Build a line from tokens and run all logical checks on it. """ self.build_tokens_line() self.report.increment_logical_line() first_line = self.lines[self.mapping[0][1][2][0] - 1] indent = first_line[:self.mapping[0][1][2][1]] self.previous_indent_level = self.indent_level self.indent_level = expand_indent(indent) if self.verbose >= 2: print(self.logical_line[:80].rstrip()) for name, check, argument_names in self._logical_checks: if self.verbose >= 4: print(' ' + name) for result in self.run_check(check, argument_names): offset, text = result if isinstance(offset, tuple): orig_number, orig_offset = offset else: for token_offset, token in self.mapping: if offset >= token_offset: orig_number = token[2][0] orig_offset = (token[2][1] + offset - token_offset) self.report_error(orig_number, orig_offset, text, check) self.previous_logical = self.logical_line def check_ast(self): try: tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) except (SyntaxError, TypeError): return self.report_invalid_syntax() for name, cls, _ in self._ast_checks: checker = cls(tree, self.filename) for lineno, offset, text, check in checker.run(): if not noqa(self.lines[lineno - 1]): self.report_error(lineno, offset, text, check) def generate_tokens(self): if self._io_error: self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) tokengen = tokenize.generate_tokens(self.readline_check_physical) try: for token in tokengen: yield token except (SyntaxError, tokenize.TokenError): self.report_invalid_syntax() def check_all(self, expected=None, line_offset=0): """ Run all checks on the input file. """ self.report.init_file(self.filename, self.lines, expected, line_offset) if self._ast_checks: self.check_ast() self.line_number = 0 self.indent_char = None self.indent_level = 0 self.previous_logical = '' self.tokens = [] self.blank_lines = blank_lines_before_comment = 0 parens = 0 for token in self.generate_tokens(): self.tokens.append(token) token_type, text = token[0:2] if self.verbose >= 3: if token[2][0] == token[3][0]: pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) else: pos = 'l.%s' % token[3][0] print('l.%s\t%s\t%s\t%r' % (token[2][0], pos, tokenize.tok_name[token[0]], text)) if token_type == tokenize.OP: if text in '([{': parens += 1 elif text in '}])': parens -= 1 elif not parens: if token_type == tokenize.NEWLINE: if self.blank_lines < blank_lines_before_comment: self.blank_lines = blank_lines_before_comment self.check_logical() self.tokens = [] self.blank_lines = blank_lines_before_comment = 0 elif token_type == tokenize.NL: if len(self.tokens) == 1: # The physical line contains only this token. self.blank_lines += 1 self.tokens = [] elif token_type == tokenize.COMMENT and len(self.tokens) == 1: if blank_lines_before_comment < self.blank_lines: blank_lines_before_comment = self.blank_lines self.blank_lines = 0 if COMMENT_WITH_NL: # The comment also ends a physical line self.tokens = [] return self.report.get_file_results() class BaseReport(object): """Collect the results of the checks.""" print_filename = False def __init__(self, options): self._benchmark_keys = options.benchmark_keys self._ignore_code = options.ignore_code # Results self.elapsed = 0 self.total_errors = 0 self.counters = dict.fromkeys(self._benchmark_keys, 0) self.messages = {} def start(self): """Start the timer.""" self._start_time = time.time() def stop(self): """Stop the timer.""" self.elapsed = time.time() - self._start_time def init_file(self, filename, lines, expected, line_offset): """Signal a new file.""" self.filename = filename self.lines = lines self.expected = expected or () self.line_offset = line_offset self.file_errors = 0 self.counters['files'] += 1 self.counters['physical lines'] += len(lines) def increment_logical_line(self): """Signal a new logical line.""" self.counters['logical lines'] += 1 def error(self, line_number, offset, text, check): """Report an error, according to options.""" code = text[:4] if self._ignore_code(code): return if code in self.counters: self.counters[code] += 1 else: self.counters[code] = 1 self.messages[code] = text[5:] # Don't care about expected errors or warnings if code in self.expected: return if self.print_filename and not self.file_errors: print(self.filename) self.file_errors += 1 self.total_errors += 1 return code def get_file_results(self): """Return the count of errors and warnings for this file.""" return self.file_errors def get_count(self, prefix=''): """Return the total count of errors and warnings.""" return sum([self.counters[key] for key in self.messages if key.startswith(prefix)]) def get_statistics(self, prefix=''): """ Get statistics for message codes that start with the prefix. prefix='' matches all errors and warnings prefix='E' matches all errors prefix='W' matches all warnings prefix='E4' matches all errors that have to do with imports """ return ['%-7s %s %s' % (self.counters[key], key, self.messages[key]) for key in sorted(self.messages) if key.startswith(prefix)] def print_statistics(self, prefix=''): """Print overall statistics (number of errors and warnings).""" for line in self.get_statistics(prefix): print(line) def print_benchmark(self): """Print benchmark numbers.""" print('%-7.2f %s' % (self.elapsed, 'seconds elapsed')) if self.elapsed: for key in self._benchmark_keys: print('%-7d %s per second (%d total)' % (self.counters[key] / self.elapsed, key, self.counters[key])) class FileReport(BaseReport): """Collect the results of the checks and print only the filenames.""" print_filename = True class StandardReport(BaseReport): """Collect and print the results of the checks.""" def __init__(self, options): super(StandardReport, self).__init__(options) self._fmt = REPORT_FORMAT.get(options.format.lower(), options.format) self._repeat = options.repeat self._show_source = options.show_source self._show_pep8 = options.show_pep8 def init_file(self, filename, lines, expected, line_offset): """Signal a new file.""" self._deferred_print = [] return super(StandardReport, self).init_file( filename, lines, expected, line_offset) def error(self, line_number, offset, text, check): """Report an error, according to options.""" code = super(StandardReport, self).error(line_number, offset, text, check) if code and (self.counters[code] == 1 or self._repeat): self._deferred_print.append( (line_number, offset, code, text[5:], check.__doc__)) return code def get_file_results(self): """Print the result and return the overall count for this file.""" self._deferred_print.sort() for line_number, offset, code, text, doc in self._deferred_print: print(self._fmt % { 'path': self.filename, 'row': self.line_offset + line_number, 'col': offset + 1, 'code': code, 'text': text, }) if self._show_source: if line_number > len(self.lines): line = '' else: line = self.lines[line_number - 1] print(line.rstrip()) print(' ' * offset + '^') if self._show_pep8 and doc: print(doc.lstrip('\n').rstrip()) return self.file_errors class DiffReport(StandardReport): """Collect and print the results for the changed lines only.""" def __init__(self, options): super(DiffReport, self).__init__(options) self._selected = options.selected_lines def error(self, line_number, offset, text, check): if line_number not in self._selected[self.filename]: return return super(DiffReport, self).error(line_number, offset, text, check) class StyleGuide(object): """Initialize a PEP-8 instance with few options.""" def __init__(self, *args, **kwargs): # build options from the command line self.checker_class = kwargs.pop('checker_class', Checker) parse_argv = kwargs.pop('parse_argv', False) config_file = kwargs.pop('config_file', None) parser = kwargs.pop('parser', None) options, self.paths = process_options( parse_argv=parse_argv, config_file=config_file, parser=parser) if args or kwargs: # build options from dict options_dict = dict(*args, **kwargs) options.__dict__.update(options_dict) if 'paths' in options_dict: self.paths = options_dict['paths'] self.runner = self.input_file self.options = options if not options.reporter: options.reporter = BaseReport if options.quiet else StandardReport for index, value in enumerate(options.exclude): options.exclude[index] = value.rstrip('/') options.select = tuple(options.select or ()) if not (options.select or options.ignore or options.testsuite or options.doctest) and DEFAULT_IGNORE: # The default choice: ignore controversial checks options.ignore = tuple(DEFAULT_IGNORE.split(',')) else: # Ignore all checks which are not explicitly selected options.ignore = ('',) if options.select else tuple(options.ignore) options.benchmark_keys = BENCHMARK_KEYS[:] options.ignore_code = self.ignore_code options.physical_checks = self.get_checks('physical_line') options.logical_checks = self.get_checks('logical_line') options.ast_checks = self.get_checks('tree') self.init_report() def init_report(self, reporter=None): """Initialize the report instance.""" self.options.report = (reporter or self.options.reporter)(self.options) return self.options.report def check_files(self, paths=None): """Run all checks on the paths.""" if paths is None: paths = self.paths report = self.options.report runner = self.runner report.start() try: for path in paths: if os.path.isdir(path): self.input_dir(path) elif not self.excluded(path): runner(path) except KeyboardInterrupt: print('... stopped') report.stop() return report def input_file(self, filename, lines=None, expected=None, line_offset=0): """Run all checks on a Python source file.""" if self.options.verbose: print('checking %s' % filename) fchecker = self.checker_class( filename, lines=lines, options=self.options) return fchecker.check_all(expected=expected, line_offset=line_offset) def input_dir(self, dirname): """Check all files in this directory and all subdirectories.""" dirname = dirname.rstrip('/') if self.excluded(dirname): return 0 counters = self.options.report.counters verbose = self.options.verbose filepatterns = self.options.filename runner = self.runner for root, dirs, files in os.walk(dirname): if verbose: print('directory ' + root) counters['directories'] += 1 for subdir in sorted(dirs): if self.excluded(subdir, root): dirs.remove(subdir) for filename in sorted(files): # contain a pattern that matches? if ((filename_match(filename, filepatterns) and not self.excluded(filename, root))): runner(os.path.join(root, filename)) def excluded(self, filename, parent=None): """ Check if options.exclude contains a pattern that matches filename. """ if not self.options.exclude: return False basename = os.path.basename(filename) if filename_match(basename, self.options.exclude): return True if parent: filename = os.path.join(parent, filename) return filename_match(filename, self.options.exclude) def ignore_code(self, code): """ Check if the error code should be ignored. If 'options.select' contains a prefix of the error code, return False. Else, if 'options.ignore' contains a prefix of the error code, return True. """ return (code.startswith(self.options.ignore) and not code.startswith(self.options.select)) def get_checks(self, argument_name): """ Find all globally visible functions where the first argument name starts with argument_name and which contain selected tests. """ checks = [] for check, attrs in _checks[argument_name].items(): (codes, args) = attrs if any(not (code and self.ignore_code(code)) for code in codes): checks.append((check.__name__, check, args)) return sorted(checks) def get_parser(prog='pep8', version=__version__): parser = OptionParser(prog=prog, version=version, usage="%prog [options] input ...") parser.config_options = [ 'exclude', 'filename', 'select', 'ignore', 'max-line-length', 'hang-closing', 'count', 'format', 'quiet', 'show-pep8', 'show-source', 'statistics', 'verbose'] parser.add_option('-v', '--verbose', default=0, action='count', help="print status messages, or debug with -vv") parser.add_option('-q', '--quiet', default=0, action='count', help="report only file names, or nothing with -qq") parser.add_option('-r', '--repeat', default=True, action='store_true', help="(obsolete) show all occurrences of the same error") parser.add_option('--first', action='store_false', dest='repeat', help="show first occurrence of each error") parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, help="exclude files or directories which match these " "comma separated patterns (default: %default)") parser.add_option('--filename', metavar='patterns', default='*.py', help="when parsing directories, only check filenames " "matching these comma separated patterns " "(default: %default)") parser.add_option('--select', metavar='errors', default='', help="select errors and warnings (e.g. E,W6)") parser.add_option('--ignore', metavar='errors', default='', help="skip errors and warnings (e.g. E4,W)") parser.add_option('--show-source', action='store_true', help="show source code for each error") parser.add_option('--show-pep8', action='store_true', help="show text of PEP 8 for each error " "(implies --first)") parser.add_option('--statistics', action='store_true', help="count errors and warnings") parser.add_option('--count', action='store_true', help="print total number of errors and warnings " "to standard error and set exit code to 1 if " "total is not null") parser.add_option('--max-line-length', type='int', metavar='n', default=MAX_LINE_LENGTH, help="set maximum allowed line length " "(default: %default)") parser.add_option('--hang-closing', action='store_true', help="hang closing bracket instead of matching " "indentation of opening bracket's line") parser.add_option('--format', metavar='format', default='default', help="set the error format [default|pylint|]") parser.add_option('--diff', action='store_true', help="report only lines changed according to the " "unified diff received on STDIN") group = parser.add_option_group("Testing Options") if os.path.exists(TESTSUITE_PATH): group.add_option('--testsuite', metavar='dir', help="run regression tests from dir") group.add_option('--doctest', action='store_true', help="run doctest on myself") group.add_option('--benchmark', action='store_true', help="measure processing speed") return parser def read_config(options, args, arglist, parser): """Read both user configuration and local configuration.""" config = RawConfigParser() user_conf = options.config if user_conf and os.path.isfile(user_conf): if options.verbose: print('user configuration: %s' % user_conf) config.read(user_conf) parent = tail = args and os.path.abspath(os.path.commonprefix(args)) while tail: if config.read([os.path.join(parent, fn) for fn in PROJECT_CONFIG]): if options.verbose: print('local configuration: in %s' % parent) break parent, tail = os.path.split(parent) pep8_section = parser.prog if config.has_section(pep8_section): option_list = dict([(o.dest, o.type or o.action) for o in parser.option_list]) # First, read the default values new_options, _ = parser.parse_args([]) # Second, parse the configuration for opt in config.options(pep8_section): if options.verbose > 1: print(" %s = %s" % (opt, config.get(pep8_section, opt))) if opt.replace('_', '-') not in parser.config_options: print("Unknown option: '%s'\n not in [%s]" % (opt, ' '.join(parser.config_options))) sys.exit(1) normalized_opt = opt.replace('-', '_') opt_type = option_list[normalized_opt] if opt_type in ('int', 'count'): value = config.getint(pep8_section, opt) elif opt_type == 'string': value = config.get(pep8_section, opt) else: assert opt_type in ('store_true', 'store_false') value = config.getboolean(pep8_section, opt) setattr(new_options, normalized_opt, value) # Third, overwrite with the command-line options options, _ = parser.parse_args(arglist, values=new_options) options.doctest = options.testsuite = False return options def process_options(arglist=None, parse_argv=False, config_file=None, parser=None): """Process options passed either via arglist or via command line args.""" if not arglist and not parse_argv: # Don't read the command line if the module is used as a library. arglist = [] if not parser: parser = get_parser() if not parser.has_option('--config'): if config_file is True: config_file = DEFAULT_CONFIG group = parser.add_option_group("Configuration", description=( "The project options are read from the [%s] section of the " "tox.ini file or the setup.cfg file located in any parent folder " "of the path(s) being processed. Allowed options are: %s." % (parser.prog, ', '.join(parser.config_options)))) group.add_option('--config', metavar='path', default=config_file, help="user config file location (default: %default)") options, args = parser.parse_args(arglist) options.reporter = None if options.ensure_value('testsuite', False): args.append(options.testsuite) elif not options.ensure_value('doctest', False): if parse_argv and not args: if options.diff or any(os.path.exists(name) for name in PROJECT_CONFIG): args = ['.'] else: parser.error('input not specified') options = read_config(options, args, arglist, parser) options.reporter = parse_argv and options.quiet == 1 and FileReport options.filename = options.filename and options.filename.split(',') options.exclude = options.exclude.split(',') options.select = options.select and options.select.split(',') options.ignore = options.ignore and options.ignore.split(',') if options.diff: options.reporter = DiffReport stdin = stdin_get_value() options.selected_lines = parse_udiff(stdin, options.filename, args[0]) args = sorted(options.selected_lines) return options, args def _main(): """Parse options and run checks on Python source.""" pep8style = StyleGuide(parse_argv=True, config_file=True) options = pep8style.options if options.doctest or options.testsuite: from testsuite.support import run_tests report = run_tests(pep8style) else: report = pep8style.check_files() if options.statistics: report.print_statistics() if options.benchmark: report.print_benchmark() if options.testsuite and not options.quiet: report.print_results() if report.total_errors: if options.count: sys.stderr.write(str(report.total_errors) + '\n') sys.exit(1) if __name__ == '__main__': _main() ================================================ FILE: utils/git-hooks/pre-commit ================================================ #!/usr/bin/env python from difflib import unified_diff as differ import os from subprocess import check_output as call, CalledProcessError import sys import shutil def read(filepath): mode = "b" if sys.platform.startswith("win") else "" data = "" with open(filepath, "rU" + mode) as fd: data = fd.read() return data def get_current_revision(): cmd = "git rev-parse --verify HEAD".split(" ") gitrev = "".join(r.strip() for r in call(cmd).split("\n") if r.strip()) return gitrev def get_commit_files(): gitrev = get_current_revision() cmd = "git diff-index --cached %s" % gitrev cmd = cmd.split(" ") gitdata = call(cmd).split("\n") gitfiles = [] for gdata in gitdata: if gdata != '': gdata = gdata.split() if gdata[4] in ['A', 'M', 'C']: gitfiles.append(gdata[5]) return gitfiles def get_project_path(): cmd = "git rev-parse --show-toplevel".split(" ") project_path = "".join(p.strip() for p in call(cmd).split("\n") if p.strip()) return project_path def get_hooks_path(): gitpath = get_project_path() hookspath = os.path.join(gitpath, 'utils', 'git-hooks') if not hookspath: hookspath = os.getcwd() return hookspath def get_subhooks(hookspath, gitpath, fname): sub_hooks = [] for root, folders, files in os.walk(hookspath): for filename in files: hookpath = os.path.join(gitpath, root, filename) if filename.startswith(fname) and filename != fname: sub_hooks.append(hookpath) return sub_hooks def check_for_update(): fname = os.path.basename(__file__) gitpath = get_project_path() old_path = os.path.join(gitpath, '.git', 'hooks', fname) new_path = os.path.join(gitpath, 'utils', 'git-hooks', fname) old_data = read(old_path) new_data = read(new_path) diff = list(differ(old_data.splitlines(), new_data.splitlines(), old_path, new_path)) changed = '\n'.join(diff) if changed: shutil.copyfile(new_path, old_path) shutil.copymode(new_path, old_path) shutil.copystat(new_path, old_path) print >> sys.stderr, "%s updated. Please re-run." % fname sys.exit(1) def run_sub_hooks(): fname = os.path.basename(__file__) gitrev = get_current_revision() gitfiles = get_commit_files() gitpath = get_project_path() hookspath = get_hooks_path() sub_hooks = get_subhooks(hookspath, gitpath, fname) stop_commit = False for hookpath in sub_hooks: for gitfile in gitfiles: fpath = os.path.join(gitpath, gitfile) cmd = [hookpath, fpath] try: output = call(cmd) except CalledProcessError as e: print e sys.exit(1) if output: print output stop_commit = True if stop_commit: print "Please re-run commit to capture changes" sys.exit(1) if __name__ == "__main__": check_for_update() run_sub_hooks() ================================================ FILE: utils/git-hooks/pre-commit_check-for-cpp-style ================================================ #!/usr/bin/env python from cpplint.cpplint import main if __name__ == "__main__": main() ================================================ FILE: utils/git-hooks/pre-commit_check-for-pdb ================================================ #!/usr/bin/env python """ Usage: check-for-pdb """ from difflib import unified_diff as differ import sys from xml.etree import cElementTree as ElementTree def check_for_pdb(filepath): mode = "b" if sys.platform.startswith('win') else "" has_pdb_string = "" with open(filepath, "rU" + mode) as fd: has_pdb_string = fd.read() if "import pdb" in has_pdb_string: print >> sys.stderr, "Error: '%s' has a pdb" % filepath sys.exit(1) if __name__ == "__main__": import sys argv = sys.argv[1:] if len(sys.argv) > 1 else [] if not argv: print __doc__ sys.exit(1) else: filepath = argv[0] extensions_to_convert = ('py') if filepath.endswith(extensions_to_convert): check_for_pdb(filepath) ================================================ FILE: utils/git-hooks/pre-commit_pep8 ================================================ #!/usr/bin/env python """ Usage: check-for-pep8 """ from difflib import unified_diff as differ import sys from xml.etree import cElementTree as ElementTree from pep8 import _main as main def check_for_pep8(filepath): main() mode = "b" if sys.platform.startswith('win') else "" has_pdb_string = "" with open(filepath, "rU" + mode) as fd: has_pdb_string = fd.read() if "import pdb" in has_pdb_string: print >> sys.stderr, "Error: '%s' has a pdb" % filepath sys.exit(1) if __name__ == "__main__": import sys argv = sys.argv[1:] if len(sys.argv) > 1 else [] if not argv: print __doc__ sys.exit(1) else: filepath = argv[0] extensions_to_convert = ('py') if filepath.endswith(extensions_to_convert): check_for_pep8(filepath) ================================================ FILE: utils/git-hooks/pre-commit_pretty-xml ================================================ #!/usr/bin/env python """ Usage: pretty-xml """ from difflib import unified_diff as differ import os import re import sys from xml.etree import cElementTree as ElementTree def pretty_xml(filepath): mode = "b" if sys.platform.startswith('win') else "" ugly_xml_string = "" with open(filepath, "rU" + mode) as fd: ugly_xml_string = fd.read() patt = r'>\n\s+([^<>\s].*?)\n\s+\g<1> 1 else [] if not argv: print __doc__ sys.exit(1) else: filepath = argv[0] extensions_to_convert = ('vcxproj', 'vcxproj.filters', 'csproj', 'xml') if filepath.endswith(extensions_to_convert): pretty_xml(filepath) ================================================ FILE: utils/git-hooks/pre-commit_strip-eol-spaces ================================================ #!/usr/bin/env python """ Usage: strip-eol-spaces """ from difflib import unified_diff as differ import sys from xml.etree import cElementTree as ElementTree def strip_eol_spaces(filepath): mode = "b" if sys.platform.startswith('win') else "" extra_space_string = "" with open(filepath, "rU" + mode) as fd: extra_space_string = fd.read() compact_string = extra_space_string.rstrip() diff = list(differ(extra_space_string.splitlines(), compact_string.splitlines(), filepath, filepath)) changed = '\n'.join(diff) if changed: with open(filepath, 'w' + mode) as fd: fd.write(compact_string) print "Striped extra EOL Spaces:", filepath if __name__ == "__main__": import sys argv = sys.argv[1:] if len(sys.argv) > 1 else [] if not argv: print __doc__ else: filepath = argv[0] extensions_to_convert = ('c', 'cpp', 'h', 'hpp', 'cs', 'py', 'vcxproj', 'vcxproj.filters', 'xml') if filepath.endswith(extensions_to_convert): strip_eol_spaces(filepath) ================================================ FILE: utils/git-hooks/pre-commit_tabs-to-spaces ================================================ #!/usr/bin/env python """ Usage: tabs-to-spaces """ from difflib import unified_diff as differ import sys from xml.etree import cElementTree as ElementTree def tabs_to_spaces(filepath): mode = "b" if sys.platform.startswith('win') else "" tabbed_string = "" with open(filepath, "rU" + mode) as fd: tabbed_string = fd.read() special = ('vcxproj', 'vcxproj.filters', 'xml') spacing = 4 if not filepath.endswith(special) else 2 untabbed_string = tabbed_string.expandtabs(spacing) diff = list(differ(tabbed_string.splitlines(), untabbed_string.splitlines(), filepath, filepath)) changed = '\n'.join(diff) if changed: with open(filepath, 'w' + mode) as fd: fd.write(untabbed_string) print "Tabs to Spaces:", filepath if __name__ == "__main__": import sys argv = sys.argv[1:] if len(sys.argv) > 1 else [] if not argv: print __doc__ else: filepath = argv[0] extensions_to_convert = ('c', 'cpp', 'h', 'hpp', 'cs', 'py', 'vcxproj', 'vcxproj.filters', 'xml') if filepath.endswith(extensions_to_convert): tabs_to_spaces(filepath)